SwiftUI @FocusState在ScrollView中不起作用

huangapple go评论73阅读模式
英文:

SwiftUI @FocusState not working in ScrollView

问题

这段代码运行得很好,正如它应该的那样。isFocused 反映了文本字段的焦点状态,按下按钮会隐藏键盘。

但是,将 TextField 放在 ScrollView 中会导致 @FocusState 不起作用。当点击按钮时,会打印出 "Dismiss",但键盘不会隐藏。

为什么会出现这种情况?以及如何修复?

英文:

This code works great, just as it should. isFocused reflects the focus state of the text field, and pressing the Button drops the keyboard.

struct ContentView: View {
    @State private var textInput = ""
    @FocusState private var isFocused: Bool
    
    var body: some View {
        VStack {
            TextField("Enter text", text: $textInput)
                .focused($isFocused)
            
            Button("Submit") {
                isFocused = false
            }
        }
    }
}

However, putting the TextField instead a ScrollView results in @FocusState NOT working. When the button is tapped, "Dismiss" is printed but the keyboard does not resign.

struct ContentView: View {
    @State private var textInput = ""
    @FocusState private var isFocused: Bool

    var body: some View {
        ScrollView(.vertical) {
            TextField("Enter text", text: $textInput)
                .focused($isFocused)
            Button("Dismiss") {
                isFocused = false
                print("Dismiss")
            }
        }
    }
}

Why is this the case? And how could this be fixed?

答案1

得分: 3

我目前在Xcode 15 beta 4上遇到了完全相同的问题,相同的代码在Xcode 14上运行得很好。已提交了一个错误报告,希望在未来的版本中能解决这个问题。

英文:

I'm currently experiencing the exact same problem on Xcode 15 beta 4, the same code works just fine in Xcode 14. Filed a bug report and hopefully it gets resolved in future versions.

答案2

得分: 0

Experiencing the same still with Xcode 15 beta 6.

A temporary workaround (though not ideal) is to replace your ScrollView with a VStack inside a List with listStyle set to plain, row separators set to hidden and row insets as (0, 0, 0, 0).

i.e.

struct ContentView: View {
    @State private var textInput = ""
    @FocusState private var isFocused: Bool
    
    var body: some View {
        List {
            VStack {
                TextField("Enter text", text: $textInput)
                    .focused($isFocused)
                Button("Dismiss") {
                    isFocused = false
                    print("Dismiss")
                }
            }
            .listRowSeparator(.hidden)
            .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
            .padding(.horizontal)
        }
        .listStyle(.plain)
    }
}

This should produce identical results to a ScrollView. To avoid repeating the workaround pattern, here's a quick backporting wrapper.

struct BackportScrollView<Content: View>: View {
    var content: () -> Content
    init(@ViewBuilder content: @escaping () -> Content) { self.content = content }
    
    var body: some View {
        if #available(iOS 17.0, *) {
            List {
                VStack { content() }
                    .listRowSeparator(.hidden)
                    .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                    .padding(.horizontal)
            }
            .listStyle(.plain)
        } else {
            ScrollView(.vertical) { content() }
        }
    }
}

You can use this like so:

struct ContentView: View {
    @State private var textInput = ""
    @FocusState private var isFocused: Bool
    
    var body: some View {
        BackportScrollView {
            TextField("Enter text", text: $textInput)
                .focused($isFocused)
            Button("Dismiss") {
                isFocused = false
                print("Dismiss")
            }
        }
    }
}
英文:

Experiencing the same still with Xcode 15 beta 6.

A temporary workaround (though not ideal) is to replace your ScrollView with a VStack inside a List with listStyle set to plain, row separators set to hidden and row insets as (0, 0, 0, 0).

i.e.

struct ContentView: View {
    @State private var textInput = &quot;&quot;
    @FocusState private var isFocused: Bool
    
    var body: some View {
        List {
            VStack {
                TextField(&quot;Enter text&quot;, text: $textInput)
                    .focused($isFocused)
                Button(&quot;Dismiss&quot;) {
                    isFocused = false
                    print(&quot;Dismiss&quot;)
                }
            }
            .listRowSeparator(.hidden)
            .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
            .padding(.horizontal)
        }
        .listStyle(.plain)
    }
}

This should produce identical results to a ScrollView. To avoid repeating the workaround pattern, here's a quick backporting wrapper.

struct BackportScrollView&lt;Content: View&gt;: View {
    var content: () -&gt; Content
    init(@ViewBuilder content: @escaping () -&gt; Content) { self.content = content }
    
    var body: some View {
        if #available(iOS 17.0, *) {
            List {
                VStack { content() }
                    .listRowSeparator(.hidden)
                    .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                    .padding(.horizontal)
            }
            .listStyle(.plain)
        } else {
            ScrollView(.vertical) { content() }
        }
    }
}

You can use this like so:

struct ContentView: View {
    @State private var textInput = &quot;&quot;
    @FocusState private var isFocused: Bool
    
    var body: some View {
        BackportScrollView {
            TextField(&quot;Enter text&quot;, text: $textInput)
                .focused($isFocused)
            Button(&quot;Dismiss&quot;) {
                isFocused = false
                print(&quot;Dismiss&quot;)
            }
        }
    }
}

huangapple
  • 本文由 发表于 2023年7月23日 13:41:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76746778.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定