FirstResponder & :onCommit on TextField in SwiftUI - swiftui

In a SwiftUI app, I need to set the focus on a TextField and bring the keyboard automatically, in standard Swift this would be done with:
field.becomeFirstResponder()
But this does not seem to exist in SwiftUI.
I found a work around here.
But, my field uses :onCommit; which is not in the sample code.
What is the way to set the :onCommit functionality when using UIViewRepresentable ?

iOS 15+ has a solution for this.
#FocusState combined with the focused(_:) modifier can be used to control first responder status for textfields.
struct ExampleView: View {
#FocusState private var isFocused: Bool
#State private var textInput = ""
var body: some View {
TextField("Example", text: $textInput)
.focused($isFocused)
Button("Confirm") {
if textInput {
isFocused = true
}
}
}
}

For iOS15
There is a solution implemented by apple (as mentioned by #AlphaWulf)
For iOS14
In my opinion, the best approach is to implement your own version of TextField using the UIRepresentable protocol. This might sound like something difficult but it is actually quite simple.
Why it is better to implement your own text field over the solutions using view hierarchy introspection?
One is that a solution based on traversing underlying views is hacky by nature and even a minor iOS version update might break it.
Secondly, in a real-world app, you will want to set additional things on the text field (like return button type and supplementary view) but Apple didn't make a way of doing so and you will be forced to wrap a UITextField in any case.
https://blog.agitek.io/swiftui-2-first-responder-b6a828243268
In this post I have a detailed solution that is similar to what Apple has implemented in SwiftUI 3.

There is an open-source project for your needs, at https://github.com/mobilinked/MbSwiftUIFirstResponder
TextField("Name", text: $name)
.firstResponder(id: FirstResponders.name, firstResponder: $firstResponder, resignableUserOperations: .all)
TextEditor(text: $notes)
.firstResponder(id: FirstResponders.notes, firstResponder: $firstResponder, resignableUserOperations: .all)

Related

Name-Field in SwiftUI

How can you make a TextField, that knows it's a Name-Field?
I need the Phone to suggest the User His Name when he taps my TextField.
Any ideas how to do that in SwiftUI?
Thanks for your help!
Boothosh
It sounds like you might be referring to one of the textContentType options.
For example:
struct ContentView: View {
#State private var textFieldContent = ""
var body: some View {
TextField("User name", text: $textFieldContent)
.textContentType(.givenName)
}
}
In this example, the system will suggest a given name for the field based on the contact card set to be the device's owner. On iOS, you can see a list of the UITextContentType options here. On macOS, there's a similar list here, albeit with many fewer options.

Implementing Swift UI Picker

What is the correct way to implement a Picker component with specific logic within a Section element?
I would like to have each type displayed in a separate row.
var types = ["Books", "Films", "Music"]
var body: some View {
Form {
Section(header: Text("Type")) {
TextField("Type", text: $newCategoryType)
// Picker
}
}
}
First you must have a #State property that can be updated based on what selection the user makes, say in this case we have this
#State private var selectedType = "Books"
Then you will implement a Picker SwiftUI struct as follows
Picker("Please choose a type", selection: $selectedType) {
ForEach(types, id: \.self) {
Text($0)
}
}
Note that the \.self is really important for ForEach to distinguish between each element inside the list, without which the Picker won't perform the selection action correctly.
The above is enough for doing the job of displaying each option as a row since that is the default behaviour of ForEach
Additionally if you want to customise the look and feel of the picker
you would like to see .pickerStyle() view modifier, for which the docs and examples are mentioned
Also
Tip: Because pickers in forms have this navigation behavior, it’s important you present them in a NavigationView on iOS otherwise you’ll find that tapping them doesn’t work. This might be one you create directly around the form, or you could present the form from another view that itself was wrapped in a NavigationView.

SwiftUI KeyboardShortcut with Arrow Keys

I’m having trouble using an arrow key as a .keyboardShortcut in SwiftUI. Sample iOS app:
struct ContentView: View {
#State var time: Date = Date()
var body: some View {
VStack {
Button("Press Me") {
time = Date()
}
.keyboardShortcut(KeyEquivalent.rightArrow, modifiers: [])
Text("\(time)")
}
}
}
This puts up a button that, when pressed, changes the time displayed in the text. I should be able to use the right arrow key on the keyboard and get it to work as well, but it doesn’t. If I change the keyboardShortcut line to, say, this:
.keyboardShortcut(KeyEquivalent(“a”), modifiers: [])
everything works as expected. You can press the “a” key and the time changes. If you hold down the command key, you get the system-provided HUD that shows the “a" shortcut. Change it to .rightAarrow and it shows the HUD but there’s an enclosed “?” for the shortcut, and the shortcut doesn’t fire when the arrow key is pressed.
(I’m aware I could do this using UIKit. Trying to understand why the SwiftUI version doesn’t work.)
I am attempting to accomplish the same objective in my MacOS SwiftUI app. Using your code as an example, I inserted the .keyboardShortcut(KeyEquivalent.rightArrow, modifiers: []) after my Button{} and it works fine. I then pasted your entire code into my ContentView and again it works fine. I do not know why it works in my MacOS app but not in your iOS app.
Copying my answer from this post. I wasn't able to use SwiftUI's commands to get this to work on iOS/iPadOS. However I found some luck using view controllers, and if you're using SwiftUI views then this will work with a hosting controller.
In your view controller, add the code below. The important bit is setting wantsPriorityOverSystemBehavior to true:
override var keyCommands: [UIKeyCommand]? {
let upArrow = UIKeyCommand(input: UIKeyCommand.inputUpArrow, modifierFlags: [], action: #selector(test))
upArrow.wantsPriorityOverSystemBehavior = true
return [upArrow]
}
#objc func test(_ sender: UIKeyCommand) {
print(">>> test was pressed")
}

View with FetchRequest doesn't update on change

This is my first post to this forum and really hope that somebody can help me here. I would highly appreciate any help!
I am writing my first app with Swift UI (never used UIKit before) which I want to publish later on.
This is also my first app which has CoreData implemented.
For example I use the following entities:
Family, Person
1 Person can have 1 Family
1 Family can have many Persons
My app is structured as follows:
ContentView:
Contains a TabView with 2 other views in it. A Settings View and a View with a LazyVGrid.
LazyVGrid View:
This View shows a GridItem for every Family. I get the Families with the following Fetchrequest:
#Environment(\.managedObjectContext) private var viewContext
// These are the Families from the FetchRequest
#FetchRequest(entity: Family.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Family.created, ascending: false)]
) var families: FetchedResults<Family>
Every GridItem is linking to a "FamilyDetailView" via NavigationLink. So i pass the family as the following:
NavigationLink(destination: FamilyDetailView(family: family).environment(\.managedObjectContext, self.viewContext), label: {Text("Family Name")
In the FamilyDetailView I get the Family with a property wrapper:
#State var family : Family
In this FamilyDetailView is the problem i have.
Here I also have a LazyVGrid, which shows 1 NavigationLink for every Person in the Family in a GridItem . In this GridItem I also show for example the "name" of the Person.
When tapping the NavigationLink i get to the last View, the PersonDetailView. This View gets the Person which is also an entity which has a relationship to the Family Entity.
I pass it as the follow:
NavigationLink(
destination: PersonDetailView(person: person),
label: {Text("Person")})
In the PersonDetailView I now change the name of the person and save the changed to CoreData.
The change is saved without a problem, the problem is that when I go back, using the topleading back button from the NavigationView, the Views are not updated. I have to restart the App to see the changes..
I know that the Problem has to be with passing the Data, but I cant figuring out what I did wrong.
I really appreciate everyone trying to help me!
Thank you very very much!!
CoreData objects are reference types, i.e. classes, conformed to ObservableObject protocol, so instead of state wrap corresponding property with ObservedObject, like
#ObservedObject var family : Family
Thank you very much, this helped me a lot. The FamilyDetail View is updating with that fix. Just one thing does not update. I have a "FamilyRowView" which is display for every GridItem in my Family LazyVGrid. This now also gets uses "#ObservedObject var family : Family" but still doent update. Any ideas for that? Thank you so much!
FamilyListView:
NavigationLink(destination: FamilyDetailView(family: family)
.environment(\.managedObjectContext, self.viewContext),
label: {**FamilyRowView**(family: family)
.environment(\.managedObjectContext, self.viewContext)})
FamilyRowView:
struct FamilyRowView: View {
#Environment(\.managedObjectContext) private var viewContext
#ObservedObject var family : Family
var body: some View {
It seems like to be the same as the other views that are working not but it doesnt update the View..

Create floating view with custom outline like in Notability

How to create such a view floating view with a custom boarder as shown in the picture? And such that it disappears as soon as the user clicks outside of the view.
Normally you would do that with a Popover like this:
#State var isPresented = false
var body: some View {
Button(action: {
self.isPresented = true
}) {
Text("Press me")
}.popover(isPresented: $isPresented, arrowEdge: .top) {
Text("Pop!") // You can put you own custom view here for the popover
}
}
Although it works as intended on the iPad (and I believe tvOS too, but I haven't tested it), it does not work properly with the current version of SwiftUI (as of 10/12/2019) on iPhones. Currently, the above code will just result in a somewhat glitchy modal on an iPhone, which I don't think is the intended function of it on iPhones. Apple's documentation for popover isn't very helpful right now, but here it is anyway.
For you information .popover is unabailable in tvOS.