I am trying to pass a core data field into a TextField so I can edit it. What am I missing here. How can I feed userProfile[0].alias ?? "" into $alias so it can be seen and edited in the text field?
private var userProfile: FetchedResults<UserProfile>
#State var alias: String = ""
Section(header: Text("Alias")) {
Print("\(userProfile[0].alias ?? "")")
TextField("your online identifier", text: $alias)
}
If you are sure about userProfile[0] then here is a possible approach
Section(header: Text("Alias")) {
Print("\(userProfile.first.alias ?? "")")
TextField("your online identifier", text: $alias, onCommit: {
guard let profile = userProfile.first else { return }
profile.alias = alias
try? viewContext.save()
})
.onAppear {
alias = userProfile.first.alias ?? ""
}
}
Related
I have been working with xcode 12 and swiftui. In my app I have textFiel with a localizable placeholder in Spanish and English, I switch to xcode 13 and it doesn't show me my localizable placeholder
this only happens in TextField, with SecureField it does not happen even with Text
this is my code
struct ContentView: View {
#State var email:String = ""
var body: some View {
VStack () {
TextField("login5", text: self.$email)
.autocapitalization(.none)
.padding()
.background(RoundedRectangle(cornerRadius: 50).stroke(Color("grayColor")))
}.padding(.horizontal, 20)
}
}
Localizable.strings
"login5" = "Correo eléctronico";
with SecureField in ios15, you can use the prompt parameter to get your localized string:
SecureField("purpose", text: $password, prompt: Text("login6"))
or using the label:
SecureField(text: $password) {
Text("login6")
}
EDIT1:
This is the test code I'm using to show a working localized TextField and SecureField.
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
#State var email = ""
#State var password = ""
#State var isVisible = false
var body: some View {
VStack (spacing: 55) {
Button(action: { isVisible.toggle() }) {
Text("Toggle isVisible")
}
TextField("login5", text: $email).border(.black)
if isVisible {
TextField("login6", text: $password).border(.green)
} else {
SecureField("password", text: $password, prompt: Text("login6")).border(.red)
}
}.padding(.horizontal, 20)
}
}
Test Localizable.strings file.
"login5" = "hola login5";
"login6" = "contraseña";
EDIT2: alternative approach of manually using LocalizedStringKey,
TextField(LocalizedStringKey("login5"), text: $email)
Your main Problem is, like workingdog already said, you need to use text: $variable.
That means for you declare your variable as #State var password = "" and use it like this..
struct ContentView: View {
#State var password = ""
...
if self.visible{
TextField("login6", text: $password)
....
} else {
SecureField("login6", text: $password)
....
}
}
Btw. next time post your code as code not as picture. Its easier to help you :)
Hope I understand your problem correctly and this will be your solution.
I've had the exact same problem, going from Xcode 12 to 13. All of a sudden some (not all) of my text fields no longer show localized string. I was able to fix the problem by forcing:
TextField(LocalizedString("usernameLabel"), text: $username)
Instead of
Textfield("usernameLabel", text: $username)
I'm currently developing an application using SwiftUI.
I'm having an error The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
There are some articles about the same error and I tried to solve this, but I can't do it in my case...
would someone mind advising me on how to solve this?
Here is the code:
EDITED
import SwiftUI
struct InfoView: View {
#EnvironmentObject var appState: AppState
var body: some View {
Button(action: {
appState.makeGetCallInfo()
}) {
Text("Hit API")
}
ForEach(appState.arrayInfos ?? []){ info in
VStack{
Group{
Text(String(info.id))
Text(String(info.no_1))
Text(String(info.no_2))
Text(info.name_1)
Text(info.name_2 ?? "")
Text(info.name_3)
Text(info.name_4 ?? "")
Text(String(info.no_3 ?? 0))
Text(info.name_5 ?? "")
Text(String(info.no_4 ?? 0))
}
Group{
Text(String(info.no_5 ?? 0))
Text(info.name_6 ?? "")
Text(String(info.no_7))
Text(String(info.no_8))
Text(info.name_7 ?? "")
Text(info.name_8)
Text(info.memo ?? "")
Text(info.created_at)
Text(info.updated_at)
Text(String(info.userId))
}
Divider()
.padding()
}
}
}
}
If I reduce the number of subviews in VStack like below, it works well. the error disappears
import SwiftUI
struct InfoView: View {
#EnvironmentObject var appState: AppState
var body: some View {
ForEach(appState.arrayInfos ?? []){ info in
VStack{
Group{
Text(String(info.id))
Text(String(info.no_1))
Text(String(info.no_2))
Text(info.name_1)
Text(info.name_2 ?? "")
Text(info.name_3)
Text(info.name_4 ?? "")
Text(String(info.no_3 ?? 0))
Text(info.name_5 ?? "")
Text(String(info.no_4 ?? 0))
}
Divider()
.padding()
}
}
}
}
AppState.swift
import SwiftUI
import Foundation
import Combine
class AppState: ObservableObject {
#Published var arrayInfos:[Infos]?
func makeGetCallInfo() {
// Set up the URL request
let endpoint: String = "https://sample.com/api/info/?user=1"
guard let url = URL(string: endpoint) else {
print("Error: cannot create URL")
return
}
var urlRequest = URLRequest(url: url)
urlRequest.addValue("token xxxxxxxxxxxxxxxxxxx", forHTTPHeaderField: "authorization")
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
DispatchQueue.main.async {
do{ self.arrayInfos = try JSONDecoder().decode([Infos].self, from: responseData)
}catch{
print("Error: did not decode")
return
}
}
}
task.resume()
}
JsonModel.swift
import Foundation
struct Infos: Codable,Identifiable {
var id: Int
var no_1: Int
var no_2: Int
var name_1: String
var name_2: String?
var name_3: String
var name_4: String?
var no_3: Int?
var name_5: String?
var no_4: Int?
var no_5: Int?
var name_6: String?
var no_7: Int
var no_8: Int
var name_7: String?
var name_8: String
var memo: String?
var created_at: String
var updated_at: String
var userId: Int
}
Xcode: Version 12.0.1
Your VStack contains too many subviews, it is allowed maximum 10. So group them by less amount, like below
VStack{
Group {
Text(String(info.id))
Text(String(info.no_1))
Text(String(info.no_2))
Text(info.name_1)
Text(info.name_2 ?? "")
Text(info.name_3)
Text(info.name_4 ?? "")
Text(String(info.no_3 ?? 0))
Text(info.name_5 ?? "")
Text(String(info.no_4 ?? 0))
}
Group {
Text(String(info.no_5 ?? 0))
Text(info.name_6 ?? "")
Text(String(info.no_7))
Text(String(info.no_8))
Text(info.name_7 ?? "")
Text(info.name_8)
Text(info.memo ?? "")
Text(info.created_at)
Text(info.updated_at)
Text(String(info.userId))
}
Divider()
.padding()
}
Note: it is preferable to separate such big containers into dedicated view and just create it here.
Break it down into smaller views. For example, make every Group a separate sub View.
Pass the info object from your loop into the subview
I'm using SwiftUI and create a SlideMenu, I want to rename my menu item. I do it by change Text to TextField but the TextField disabled by default. What wrong with TextField, I unable to click on it or is there any way to rename without swap Text <-> TextField?
struct CellView: View {
#State private var isRename = false
#State private var newName: String = ""
var menuItem: MenuItem
var body: some View {
HStack {
Image(systemName: "folder")
.foregroundColor(.accentColor)
if isRename {
TextField(menuItem.name ?? "Unknow name", text: $newName)
}else {
Text(menuItem.name ?? "Unknow name")
}
Spacer()
}
.contextMenu {
Button(action: {
isRename = true
}) {
HStack {
Image(systemName: "pencil")
.foregroundColor(.accentColor)
Text("Rename")
}
}
}
}
}
With iOS 16, there's a built-in RenameButton view and a .renameAction{} modifier:
RenameButton()
.renameAction {
//add action here
}
I don't think it is disabled (as tested provided code), you just see placeholder and TextField is not in focus by default.
So here is possible solution (based on CustomTextField from this answer)
if isRename {
CustomTextField(text: $newName,
nextResponder: .constant(nil),
isResponder: .constant(true),
isSecured: false,
keyboard: .default)
}else {
Text(menuItem.name ?? "Unknow name")
}
Spacer()
}
.contextMenu {
Button(action: {
newName = menuItem.name ?? "Unknown name"
isRename = true
}) {
Note: it is not shown what is MenuItem but consider possibility to bind text field directly to menuItem.name
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())
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)
}
}
}