Direct and reflect focus in SwiftUI

RSS for tag

Discuss the WWDC21 session Direct and reflect focus in SwiftUI.

View Session

Posts under wwdc21-10023 tag

10 Posts
Sort by:
Post not yet marked as solved
4 Replies
272 Views
In iOS 15 SDK you added the new FocusState API in SwiftUI. However there is no discussion or explanation anywhere that I could find, which explains: What exactly is "focus"? What isn't focus? What is the relationship between FocusState and accessibility focus? What is the relationship between whether a SecureField is being edited, and whether it's "focused"? Example: Lets say my tvOS app has an on-screen keyboard, where the user uses the remote's directional controls to move focus around to the letter buttons. To enter their password, they focus the password field, then click the center button to activate it. Now that it's active, they move focus to each letter of their password and click on each one: P... A... S... S... W... R... D... !... then they move focus to the "Submit" button and click. In this case, while the SecureField is being edited, focus moves around to a bunch of different buttons. The point of this example is that, if SecureField had a public "isBeingEdited" property, then it would be TRUE even while the field is not focused. However most Workday's designers interpret "focused" as being totally equivalent to "isBeingEdited" because in a web browser, tabbing out of a field makes it stop being edited. What is Apple's intent here? When not using a remote or physical keyboard or screen-reader, how is focus supposed to relate to whether a field is being edited? Does this relationship change when a user now has a bluetooth keyboard connected and Full Keyboard Access is turned ON? How does this correlate with accessibility focus? I cannot find any documentation from Apple that explains what focus is, or how this is supposed to work in SwiftUI in the various different scenarios where the concept of "focus" is relevant. Do you have a link to something current that explains how it's supposed to work so that we will know if there's a bug? Last question: how can we make the iOS simulator treat the physical keyboard as if it was a bluetooth keyboard to be used for focus-based keyboard navigation?
Posted
by O_G.
Last updated
.
Post not yet marked as solved
5 Replies
11k Views
Let's say I have a SwiftUI view that's a compose experience, like composing a new email in Mail, an iMessage in Messages, or a Tweet in Twitter. When the view is loaded, I want the soft keyboard to appear automatically so that the user can type immediately without needing to manually put focus in the TextField or TextEditor by tapping on it. With the new @FocusState feature in iOS 15, I can programmatically put focus on different TextFields when the user interacts elsewhere in the UI (like pressing a button), but I can't seem to set a default focus. I tried initializing focus in the SwiftUI view's init method as well as the onAppear method attached to other parts of the view, but this didn't seem to have any effect. How can I put default focus on a TextField or TextEditor when a view is loaded?
Posted Last updated
.
Post not yet marked as solved
0 Replies
347 Views
I am trying to set the focus on the .searchable text entry field using @FocuseState and .focused($focusedField, equals: .search) technique demonstrated by Tanu in her WWDC21-10023 talk, but I'm not sure if it is a focusable element or how to programmatically set it. My attempt: //based on a technique presented in https://developer.apple.com/videos/play/wwdc2021/1002 and Paul Hudson's article https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-a-search-bar-to-filter-your-data import SwiftUI enum Field: Hashable {   case search } struct ContentView: View {   let itemsToSearch = ["John", "Mark", "Adam", "Carol", "Ismael", "Vanessa", "Aniq"]   @FocusState private var focusedField: Field?   @State private var searchText = ""   var body: some View {     NavigationView {       List {         ForEach(searchResults, id: \.self) { itemsToSearch in           NavigationLink(destination: Text(itemsToSearch)) {             Text(itemsToSearch)           }         }       }       .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always)) {         ForEach(searchResults, id: \.self) { result in           Text("\(result)").searchCompletion(result)         }      .focused($focusedField, equals: .search)         .onAppear {           focusedField = .search         }       }     }   }   var searchResults: [String] {     if searchText.isEmpty {       return itemsToSearch     } else {       return itemsToSearch.filter { $0.contains(searchText) }     }   } } struct ContentView_Previews: PreviewProvider {   static var previews: some View {     ContentView()   } } Can someone clarify if this is possible, and if so, where I am going wrong? Thanks in advance. Laith
Posted
by LaithB.
Last updated
.
Post not yet marked as solved
0 Replies
219 Views
On a MAC, the lost focus event is not triggered when the focus shifts away from the browser (This happens on all browsers (Safari, Chrome or Firefox))to another window when clicking on a search textbox or notification panel. This has been observed in:   MAC OS Big SUR Version 11.2.1 MAC OS Big SUR Version 11.5.1 MAC OS Big SUR Version 11.6 MAC OS Monterey 12.1   MAC OS High Sierra Version 10.13.6 is the only MAC OS where the lost focus event is triggered when clicking away from the browser to an OS component such as a textbox or notification panel. Is it possible to restore the lost focus event on the above versions of MAC OS?
Posted Last updated
.
Post not yet marked as solved
1 Replies
196 Views
struct ModelsByCategoryGrid: View {   @Binding var showBrowse: Bool   let models = Models()       var body: some View {     VStack { [ONE ERROR HERE SAYS "Type'(_) -> ()' cannot conform to 'View']       ForEach(ModelCategory.allCases, id: .self); { category in [2 ERRORS HERE SAY 1: "Generic parameter 'Content' could not be inferred" 2: "Missing argument for parameter 'content' in call                   //Only display the grid if the category contains items         if let modelsByCategory = models.get(category: category) {           HorizontalGrid(showbrowse: $showBrowse, title: category.label, items: modelsByCategory)         }       }     }   } }
Posted
by Joewan.
Last updated
.
Post not yet marked as solved
0 Replies
767 Views
I am designing a SwiftUI view, which is meant to be presented modally (using a sheet modifier). I want the view to have two buttons inside its own navigation bar and this somehow forced me to use NavigationView inside the body of the view. The view is going to be a simple form and one of its components is a TextField, which I want to automatically activate (set focus on), when the view appears. For that, I used the @FocusState property wrapper. In spite of using almost the same view's body structure as in another file before, this new form kept crashing whenever my preview updated, yielding a long report. Here are some essentials of that report: Code Type: X86-64 (Native) Role: Non UI Parent Process: launchd_sim [30182] Responsible Process: SimulatorTrampoline [872] Exception Type: EXC_BAD_INSTRUCTION (SIGILL) Exception Codes: 0x0000000000000001, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: SIGNAL 4 Illegal instruction: 4 Terminating Process: exc handler [30404] Thread 0 Crashed:: Dispatch queue: BSXPCCnx:com.apple.dt.xcode-previews.systemservices (BSCnx:client:com.apple.dt.uv.agent-preview-nonui-service) 0 SwiftUI 0x7fff5c970f89 static FocusState._makeProperty<A>(in:container:fieldOffset:inputs:) + 160 1 SwiftUI 0x7fff5c97141e protocol witness for static DynamicProperty._makeProperty<A>(in:container:fieldOffset:inputs:) in conformance FocusState<A> + 25 I immediately saw that there was a problem with @FocusState. Indeed, removing it stopped crashing the app. But how was the other view I mentioned working completely fine? I started comparing them. The old view's body started with a Form and that view was embedded in a NavigationView in its preview. In the new view, the NavigationView was moved into its body itself. That was the key! I prepared a little example view to illustrate this issue. This version crashes when you try to launch the preview: import SwiftUI struct Test: View {     @FocusState var focusedFieldNumber: Int?     @State private var comment = ""     var body: some View {         NavigationView {             Form {                 TextEditor(text: $comment)                     .focused($focusedFieldNumber, equals: 1)             }             .onAppear {                 DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {                     self.focusedFieldNumber = 1                 }             }         }     } } struct Test_Previews: PreviewProvider {     static var previews: some View {         Test()     } } And this version (extract) solves the problem: import SwiftUI struct Test: View { // ...     var body: some View { // Removed NavigationView.         Form { // ...         } // ...     } } struct Test_Previews: PreviewProvider {     static var previews: some View { // Added NavigationView.         NavigationView {             Test()         }     } } Why does the position of NavigationView decide whether the preview crashes or not? Where does this behavior come from? I would love to hear something from Apple Developers. Thank you!
Posted
by Oplaner.
Last updated
.
Post not yet marked as solved
2 Replies
2.6k Views
In a SwiftUI lab, I was asking about setting the focus state down a view hierarchy. The answer I got was to pass the focus state down the views as a binding. Conceptually, that made sense, so I moved on to other questions. But now that I am trying to implement it, I am having problems. In the parent view, I have something like this: @FocusState private var focusElement: UUID? Then I am setting a property like this in the child view: @Binding var focusedId: UUID? When I try to create the detail view, I'm trying this: DetailView(focusedId: $focusElement) But this doesn't work. The error I get is: Cannot convert value of type 'FocusState<UUID?>.Binding' to expected argument type 'Binding<UUID?>' What is the right way to pass down the focus state to a child view so that it can update back up to the parent view? I am trying to update from one child view, and have a TextField in a sibling view get focus.
Posted
by bsndesign.
Last updated
.
Post not yet marked as solved
0 Replies
418 Views
I want implement a UI in tvOS. The hierarchy is like this: App -> Tab Bar -> <Home, Search, Settings> When I'm in Home and want to play a VOD, I want to hide tab bar (top menu) so the video is playing without overlay. Using .isHidden on TabView result in hiding eveything because of the hierarchy. Using .focused also do nothing. How to achieve it?
Posted
by ghararym.
Last updated
.
Post not yet marked as solved
0 Replies
288 Views
I wanted to use SwiftUI to manage dynamic forms for one of my apps. The dynamic forms aren't compatible with enum, and every single usage demo or blog post on iOS 15's FocusState is using an enum. Official docs use enum My example without Enum, for you see that it's possible. But should it be done? // Focus State Example // // Created by Michael Robert Ellis on 12/7/21. // import SwiftUI struct MyObject: Identifiable, Equatable { var id: String public var value: String init(name: String, value: String) { self.id = name self.value = value } } struct ContentView: View { @State var myObjects: [MyObject] = [ MyObject(name: "aa", value: "1"), MyObject(name: "bb", value: "2"), MyObject(name: "cc", value: "3"), MyObject(name: "dd", value: "4") ] @State var focus: MyObject? var body: some View { ScrollView(.vertical) { VStack { Text("Header") ForEach(self.myObjects) { obj in Divider() FocusField(displayObject: obj, focus: $focus, nextFocus: { guard let index = self.myObjects.firstIndex(of: $0) else { return } self.focus = myObjects.indices.contains(index + 1) ? myObjects[index + 1] : nil }) } Divider() Text("Footer") } } } } struct FocusField: View { @State var displayObject: MyObject @FocusState var isFocused: Bool @Binding var focus: MyObject? var nextFocus: (MyObject) -> Void var body: some View { TextField("Test", text: $displayObject.value) .onChange(of: focus, perform: { newValue in self.isFocused = newValue == displayObject }) .focused(self.$isFocused) .submitLabel(.next) .onSubmit { self.nextFocus(displayObject) } } } Am I being dumb? Am I missing something or am I on to something?
Posted
by mellis94.
Last updated
.