SwiftUI TextField force lowercase - swiftui

I would like to use a specific TextField for url entries where the user could only enter lowercase characters, but I can't find any TextField modifier for this purpose. Is there any solution?

TextField has a .autocapitalization() method.
You can use like this without custom binding:
TextField("URL", text: $url)
.keyboardType(.URL)
.autocapitalization(.none)
For iOS 15 SwiftUI have a new .textInputAutocapitalization() method:
.textInputAutocapitalization(.never)
This means that any text input by the user will be .lowercased()

You can create a custom binding and set your state URL variable to the lowercased version of the input through it:
struct ContentView: View {
#State var url: String = ""
var body: some View {
let binding = Binding<String>(get: {
self.url
}, set: {
self.url = $0.lowercased()
})
return VStack {
TextField("Enter URL", text: binding)
}
}
}

XCODE 13
SwiftUI - IOS 15.0
FROM:
.autocapitalization(.none)
TO:
.textInputAutocapitalization(.never)
Example:
TextField("Enter URL", text: $url)
.keyboardType(.URL)
.textInputAutocapitalization(.never)

if all you want is to "end up" with a lowercase string after the user press return, you could do this:
#State var txt: String = ""
var body: some View {
TextField("", text: $txt, onEditingChanged: { _ in
self.txt = self.txt.lowercased()
})
}

a more complicated but more flexible way, is something like this:
class LowerCaseStringFormatter: Formatter {
override func string(for obj: Any?) -> String? {
guard let str = obj as? NSString else { return nil }
return str.lowercased as String
}
override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
obj?.pointee = string.lowercased() as NSString
return true
}
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
return true
}
}
and call it like this:
TextField("type something...", value: $txt, formatter: LowerCaseStringFormatter())

Related

Return Text from a SwiftUI View

I have a custom View that's basically wrapping Text with some additional functionality. E.g. formatting the text differently based on a value in the environment.
I want to use this custom view in place of a Text, i.e. I want to be able to use modifiers like .bold() on it.
Is it possible to return a Text from a View struct?
Here is 2 ways for you:
Way 1: You can use .bold() just on your CustomView!
struct CustomTextView: View {
#Environment(\.font) var environmentFont: Font?
private let boldAllowed: Bool
let string: String
private init(boldAllowed: Bool, string: String) {
self.boldAllowed = boldAllowed
self.string = string
}
init(_ string: String) {
self.init(boldAllowed: false, string: string)
}
var body: some View {
Text(string)
.font(boldAllowed ? environmentFont?.bold() : environmentFont)
.foregroundColor(Color.red) // <--- ::: some custom work here! :::
}
func bold() -> CustomTextView {
return CustomTextView(boldAllowed: true, string: string)
}
}
use case:
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, World!")
CustomTextView("Hello, World!")
.bold()
CustomTextView("Hello, World!")
}
.font(Font.title.italic())
}
}
Result:
Way 2: You can use .bold() on any View you want!
I think this is the best possible way, because it is less code and usable on any view!
struct CustomBoldTextViewModifier: ViewModifier {
#Environment(\.font) var environmentFont: Font?
func body(content: Content) -> some View {
return content
.environment(\.font, environmentFont?.bold())
}
}
extension View {
func bold() -> some View {
return self.modifier(CustomBoldTextViewModifier())
}
}
use case:
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, World!")
}
.bold()
.font(Font.title.italic())
}
}
Result:
You can manually get the view's body. Although this isn't recommended, it is possible. However a better solution may be to pass in a closure to modify your text, see 2nd solution. Both answers achieve the same thing.
Solution #1 (not recommended)
struct ContentView: View {
var body: some View {
CustomText("Custom text")
.getText()
.bold()
.environment(\.font, .largeTitle)
}
}
struct CustomText: View {
private let content: String
init(_ content: String) {
self.content = content
}
var body: some View {
// Apply whatever modifiers from environment, etc.
Text(content)
}
func getText() -> Text {
body as! Text
}
}
Here, the custom text is made bold.
Solution #2 (recommended)
This example you just pass in a closure of how to modify the already modified custom text. This is what I would recommend, plus it looks a lot cleaner.
struct ContentView: View {
var body: some View {
CustomText("Custom text") { text in
text
.bold()
.environment(\.font, .largeTitle)
}
}
}
struct CustomText<Content: View>: View {
private let content: String
private let transform: (Text) -> Content
init(_ content: String, transform: #escaping (Text) -> Content) {
self.content = content
self.transform = transform
}
var body: some View {
// Apply whatever modifiers from environment, etc.
let current = Text(content)
/* ... */
return transform(current)
}
}

SwiftUI Textfields and Ints

I started today to use SwiftUI an I want to code a small calculator. Therefore I want a textfield where the user can write a number. But the usual textfields only accepts strings
what can I do ?
If you want to force the TextField keyboard to be numeric, just do this:
TextField("Input", text: $input)
.keyboardType(.decimalPad)
where the keyboardType is a UIKeyboardType.
with SwiftUI 2.0 you could use something like this:
struct ContentView: View {
#State var theInt: Int?
#State var text = ""
var body: some View {
VStack {
TextField("enter a number", text: $text)
.onChange(of: text) {
let txt = $0.filter { "-0123456789".contains($0) }
if allowed(txt) {
theInt = Int(txt)
text = txt
} else {
text = String(txt.dropLast())
}
}
}
}
func allowed(_ str: String) -> Bool {
let num = str.starts(with: "-") ? String(str.dropFirst()) : str
return CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: num))
}
}

How should I get and set a value of UserDefaults?

I'm currently developing an application using SwiftUI.
I want to use a UserDefaults value in this app.
So I made a code below.
But in this case, when I reboot the app(the 4'th process in the process below), I can't get value from UserDefaults...
Build and Run this project.
Pless the home button and the app goes to the background.
Double-tap the home button and remove the app screen.
press the app icon and reboot the app. Then I want to get value from UserDefaults.
to resolve this problem how should I set and get a value in UserDefaults?
Here is the code:
import SwiftUI
struct ContentView: View {
#State var text = "initialText"
var body: some View {
VStack {
Text(text)
TextField( "", text: $text)
}.onAppear(){
if let text = UserDefaults.standard.object(forKey: "text" ){
self.text = text as! String
}
}
.onDisappear(){
UserDefaults.standard.set(self.text, forKey: "text")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ADD
When I add this class following the first answer, that code has a couple of errors like this, is it usual?
Xcode: Version 11.7
Swift: Swift 5
Set in a class like this your values: Bool, String(see example), Int, etc...
#if os(iOS)
import UIKit
#else
import AppKit
#endif
import Combine
#propertyWrapper struct UserDefault<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
final class UserSettings: ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
#UserDefault("myText", defaultValue: "initialText")
var myText: String {
willSet { objectWillChange.send() }
}
}
this to read:
let settings = UserSettings()
let count = settings.countSentence // default countsentence 1
this to update:
let settings = UserSettings()
settings.countSentence = 3 // default countsentence 3
Based on your code:
struct ContentView: View {
let UserDef = UserSettings()
#State var text = ""
var body: some View {
VStack {
Text(UserDef.myText)
TextField("placeholder", text: $text, onCommit: { self.UserDef.myText = self.text})
}.onAppear() {
self.text = self.UserDef.myText
}
}
}

SwiftUI: Add ClearButton to TextField

I am trying to add a ClearButton to TextField in SwiftUI when the particular TextField is selected.
The closest I got was creating a ClearButton ViewModifier and adding it to the TextField using .modifer()
The only problem is ClearButton is permanent and does not disappear when TextField is deselected
TextField("Some Text" , text: $someBinding).modifier(ClearButton(text: $someBinding))
struct ClearButton: ViewModifier {
#Binding var text: String
public func body(content: Content) -> some View {
HStack {
content
Button(action: {
self.text = ""
}) {
Image(systemName: "multiply.circle.fill")
.foregroundColor(.secondary)
}
}
}
}
Use ZStack to position the clear button appear inside the TextField.
TextField("Some Text" , text: $someBinding).modifier(ClearButton(text: $someBinding))
struct ClearButton: ViewModifier
{
#Binding var text: String
public func body(content: Content) -> some View
{
ZStack(alignment: .trailing)
{
content
if !text.isEmpty
{
Button(action:
{
self.text = ""
})
{
Image(systemName: "delete.left")
.foregroundColor(Color(UIColor.opaqueSeparator))
}
.padding(.trailing, 8)
}
}
}
}
Use .appearance() to activate the button
var body: some View {
UITextField.appearance().clearButtonMode = .whileEditing
return TextField(...)
}
For reuse try with this:
func TextFieldUIKit(text: Binding<String>) -> some View{
UITextField.appearance().clearButtonMode = .whileEditing
return TextField("Nombre", text: text)
}
=== solution 1(best): Introspect https://github.com/siteline/SwiftUI-Introspect
import Introspect
TextField("", text: $text)
.introspectTextField(customize: {
$0.clearButtonMode = .whileEditing
})
=== solution 2: ViewModifier
public struct ClearButton: ViewModifier {
#Binding var text: String
public init(text: Binding<String>) {
self._text = text
}
public func body(content: Content) -> some View {
HStack {
content
Spacer()
Image(systemName: "multiply.circle.fill")
.foregroundColor(.secondary)
.opacity(text == "" ? 0 : 1)
.onTapGesture { self.text = "" } // onTapGesture or plainStyle button
}
}
}
Usage:
#State private var name: String
...
Form {
Section() {
TextField("NAME", text: $name).modifier(ClearButton(text: $name))
}
}
=== solution 3: global appearance
UITextField.appearance().clearButtonMode = .whileEditing
You can add another Binding in your modifier:
#Binding var visible: Bool
then bind it to opacity of the button:
.opacity(visible ? 1 : 0)
then add another State for checking textField:
#State var showClearButton = true
And lastly update the textfield:
TextField("Some Text", text: $someBinding, onEditingChanged: { editing in
self.showClearButton = editing
}, onCommit: {
self.showClearButton = false
})
.modifier( ClearButton(text: $someBinding, visible: $showClearButton))
Not exactly what you're looking for, but this will let you show/hide the button based on the text contents:
HStack {
if !text.isEmpty {
Button(action: {
self.text = ""
}) {
Image(systemName: "multiply.circle")
}
}
}
After initializing a new project we need to create a simple view modifier which we will apply later to our text field. The view modifier has the tasks to check for content in the text field element and display a clear button inside of it, if content is available. It also handles taps on the button and clears the content.
Let’s have a look at that view modifier:
import SwiftUI
struct TextFieldClearButton: ViewModifier {
#Binding var text: String
func body(content: Content) -> some View {
HStack {
content
if !text.isEmpty {
Button(
action: { self.text = "" },
label: {
Image(systemName: "delete.left")
.foregroundColor(Color(UIColor.opaqueSeparator))
}
)
}
}
}
}
The code itself should be self explanatory and easy to understand as there is no fancy logic included in our tasks.
We just wrap the textfield inside a HStack and add the button, if the text field is not empty. The button itself has a single action of deleting the value of the text field.
For the clear icon we use the delete.left icon from the SF Symbols 2 library by Apple, but you could also use another one or even your own custom one.
The binding of the modifier is the same as the one we apply to the text field. Without it we would not be able to check for content or clear the field itself.
Inside the ContentView.swift we now simply add a TextField element and apply our modifier to it — that’s all!
import SwiftUI
struct ContentView: View {
#State var exampleText: String = ""
var body: some View {
NavigationView {
Form {
Section {
TextField("Type in your Text here...", text: $exampleText)
.modifier(TextFieldClearButton(text: $exampleText))
.multilineTextAlignment(.leading)
}
}
.navigationTitle("Clear button example")
}
}
}
The navigation view and form inside of the ContentView are not required. You could also just add the TextField inside the body, but with a form it’s much clearer and beautiful. 🙈
And so our final result looks like this:
I found this answer from #NigelGee on "Hacking with Swift".
.onAppear {
UITextField.appearance().clearButtonMode = .whileEditing
}
It really helped me out.
Simplest solution I came up with
//
// ClearableTextField.swift
//
// Created by Fred on 21.11.22.
//
import SwiftUI
struct ClearableTextField: View {
var title: String
#Binding var text: String
init(_ title: String, text: Binding<String>) {
self.title = title
_text = text
}
var body: some View {
ZStack(alignment: .trailing) {
TextField(title, text: $text)
Image(systemName: "xmark.circle.fill")
.foregroundColor(.secondary)
.onTapGesture {
text = ""
}
}
}
}
struct ClearableTextField_Previews: PreviewProvider {
#State static var text = "some value"
static var previews: some View {
Form {
// replace TextField("Original", text: $text) with
ClearableTextField("Clear me", text: $text)
}
}
}

How to detect changes to the BindableObjects from the View?

In UIKit, I would have code like this:
#IBOutlet weak var itemNameField: UITextField!
#IBAction func itemNameFieldDone(_ sender: UITextField) {
thisItem.myName = sender.text ?? thisItem.myName
thisItem.modified()
}
In the model object:
func modified() {
dateModified = Date()
let cds = FoodyDataStack.thisDataStack
uuidUser = cds.uuidUser
uuidFamily = cds.uuidFamily
}
In SwiftUI:
TextField($thisItem.myName)
Declarative, nice and short. SwiftUI takes care of updating the myName property as the user types in the TextField, but how do I get the dateModified property to update at the same time?
Use the TextField initializer that includes onEditingChanged and include whatever updating code you need in the closure.
TextField($thisCategory.myName, placeholder: nil, onEditingChanged: { (changed) in
self.thisCategory.modified()
self.dataStack.didChange.send(self.dataStack)
}).textFieldStyle(.roundedBorder)
iOS 14
There is a new modifier called onChange to detect changes of any state:
struct ContentView: View {
#State var text: String = ""
var body: some View {
TextField("Title", text: $text)
.onChange(of: text, perform: { value in
print(text)
})
}
}
You can add didSet observer to myName property in your item type declaration and then call modified from there:
var myName: String = "" {
didSet {
self.modified()
}
}