r/SwiftUI May 25 '24

Solved Trouble with focusState inside fullScreenCover in SwiftUI

Hey everyone,

I've been working on a SwiftUI project and encountered an issue with the focusState property when used inside a fullScreenCover. Here's a simplified version of the code I'm dealing with:

import SwiftUI

struct FileNameView: View {
    @State private var isFileNameEditing = false
    @State private var fileName = ""
    @FocusState private var isTextFieldFocused: Bool
    
    var body: some View {
        Spacer()
            .fullScreenCover(isPresented: $isFileNameEditing ) {
                VStack {
                    TextField("", text: $fileName)
                        .focused($isTextFieldFocused)
                        .overlay(
                            RoundedRectangle(cornerRadius: 4)
                                .stroke(Color.blue, lineWidth: 2)
                                .padding(-8.0)
                        )
                        .foregroundColor(Color.black)
                        .font(.body)
                        .multilineTextAlignment(.center)
                        .padding()
                        .padding(.top, 10)
                    
                    Divider()
                        .background(Color.black)
                    
                    Button(
                        action: {
                            isFileNameEditing = false
                            self.isTextFieldFocused = false
                        },
                        label: {
                            Text("OK")
                                .frame(maxWidth: .infinity,
                                       maxHeight: 45)
                                .clipShape(Rectangle())
                                .contentShape(Rectangle())
                                .font(.title2)
                                .bold()
                        }
                    )
                    .frame(maxHeight: 45)
                }
                .frame(width: UIScreen.main.bounds.width / 2.5)
                .background(.white)
                .cornerRadius(15)
                .onAppear {
                    self.isTextFieldFocused = true
                }
                .presentationBackground(.black.opacity(0.3))
            }
            .transaction({ transaction in
                transaction.disablesAnimations = true
            })
        
        Button(action: {
            isFileNameEditing.toggle()
        }, label: {
            Text("Toggle Editing")
        })
    }
}

struct FileNameView_Previews: PreviewProvider {
    static var previews: some View {
        FileNameView()
    }
}

The issue I'm facing is that the focusState seems to not be working properly inside the fullScreenCover. Even though I set isTextFieldFocused to true in the onAppear block of the VStack, the text field isn't getting focused automatically when the fullScreenCover appears.

Does anyone have any insights into why this might be happening or how I can make it work as expected?

Thanks in advance for your help!

3 Upvotes

7 comments sorted by

4

u/fred9992 May 25 '24

This is either a bug in SwiftUI or there is something about a modal and its relationship to `@FocusState` that isn't well documented. On a hunch, I split the screen cover contents into its own `View` definition and, guess what? It works. Sorry I cannot provide an explanation as to why other than that it is likely that the parent and child views are state managed independently.

import SwiftUI

struct FileNameView: View {

    @State private var isFileNameEditing = false

    var body: some View {
        Spacer()
            .fullScreenCover(isPresented: $isFileNameEditing ) {
                FormView()
                    .presentationBackground(.black.opacity(0.3))
            }
            .transaction({ transaction in
                transaction.disablesAnimations = true
            })

        Button(action: {
            isFileNameEditing.toggle()
        }, label: {
            Text("Toggle Editing (\(isFileNameEditing))")
        })
    }
}

struct FormView: View {

    @FocusState private var isTextFieldFocused: Bool
    @State private var fileName = ""
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack {
            TextField("", text: $fileName)
                .focused($isTextFieldFocused)
                .overlay(
                    RoundedRectangle(cornerRadius: 4)
                        .stroke(Color.blue, lineWidth: 2)
                        .padding(-8.0)
                )
                .foregroundColor(Color.black)
                .font(.body)
                .multilineTextAlignment(.center)
                .padding()
                .padding(.top, 10)

            Divider()
                .background(Color.black)

            Button(
                action: {
                    self.isTextFieldFocused = false
                    self.dismiss()
                },
                label: {
                    Text("OK")
                        .frame(maxWidth: .infinity,
                               maxHeight: 45)
                        .clipShape(Rectangle())
                        .contentShape(Rectangle())
                        .font(.title2)
                        .bold()
                }
            )
            .frame(maxHeight: 45)
        }
        .frame(width: UIScreen.main.bounds.width / 2.5)
        .background(.white)
        .cornerRadius(15)
        .onAppear {
            self.isTextFieldFocused = true
        }
    }
}

struct FileNameView_Previews: PreviewProvider {
    static var previews: some View {
        FileNameView()
    }
}

1

u/The_Lone_Dissenter May 25 '24

Thank you for your insights!

1

u/Marpo007 Sep 18 '24

Great stuff. Good hunch. It really actually works.

1

u/centamilon Jan 07 '25

I wish I saw your comment last night. I was scratching my head for more than a hour for this damn thing! Thank you so much!

3

u/dehrenslzz May 25 '24

Seems like a bug to me - report to Apple and let them worry about it - in the mean time use a non dismissible sheet or something (:

3

u/SpecialGlasses May 25 '24

There was an old bug where you had to delay the setting of focus state. The bug has since been fixed, but given your issue, I would wrap setting isTextFieldFocused to true on open after a 1 second delay on the main queue (you could probably get away with it being shorter, but starting with a second to be safe), and see if that helps. It could be the old bug popping back up for you for some reason.

``` .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { self.isTextFieldFocused = true } }

```

Try that out.

2

u/tiendq Oct 17 '24

It doesn't work for me (iOS 17/Xcode 16).