swift - TextField with animation crashes app and looses focus
问题描述
Minimal reproducible example:
In SceneDelegate.swift
:
let contentView = Container()
In ContentView.swift
:
struct SwiftUIView: View {
@State var text: String = ""
var body: some View {
VStack {
CustomTextFieldView(text: $text)
}
}
}
struct Container: View {
@State var bool: Bool = false
@State var text: String = ""
var body: some View {
Button(action: {
self.bool.toggle()
}) {
Text("Sheet!")
}
.sheet(isPresented: $bool) {
SwiftUIView()
}
}
}
In CustomTextField.swift
:
struct CustomTextFieldView: View {
@Binding var text: String
@State var editing: Bool = false
var body: some View {
Group {
if self.editing {
textField
.background(Color.red)
} else {
ZStack(alignment: .leading) {
textField
.background(Color.green)
Text("Placeholder")
}
}
}
.onTapGesture {
withAnimation {
self.editing = true
}
}
}
var textField: some View {
TextField("", text: $text)
}
Problem:
After running the above code and focusing the text field, the app crashes. Some things I noticed:
- If I remove the
withAnimation
code, or theZStack
inCustomTextField
file, the app doesn't crash, but the TextField looses focus. - If I remove the
VStack
inSwiftUIView
, the app doesn't crash, but the TextField looses focus. - If I use a NavigationLink or present the TextField without a sheet, the app doesn't crash, but the TextField looses focus.
Questions:
- Is this a problem in the current version of SwiftUI?
- Is there a solution to this problem using SwiftUI? I want to stay out of ViewRepresentables as much as possible.
- How can I keep the focus of the TextField after the body is recalculated because of a change in state?
解决方案
- How can I keep the focus of the TextField after the body is recalculated because of a change in state?
You have two of them. Two different TextField could not be in editing state at the same time.
The approach suggested by Asperi is the only possible.
The reason, why your code crash is not easy explain, but expected in current SwiftUI.
You have to understand, that Group is not a standard container, it just like a "block" on which you can apply some modifiers. Removing Group and using wraping body in ViewBuilder
struct CustomTextFieldView: View {
@Binding var text: String
@State var editing: Bool = false
@ViewBuilder
var body: some View {
if self.editing {
TextField("", text: $text)
.background(Color.red)
.onTapGesture {
self.editing.toggle()
}
} else {
ZStack(alignment: .leading) {
TextField("", text: $text)
.background(Color.green)
.onTapGesture {
self.editing.toggle()
}
}
}
}
}
the code will stop to crash, but there is other issue, the keyboard will dismiss immediately. That is due the tap gesture applied.
So, believe or not, you have to use ONE TextField ONLY.
struct CustomTextFieldView: View {
@Binding var text: String
@State var editing = false
var textField: some View {
TextField("", text: $text, onEditingChanged: { edit in
self.editing = edit
})
}
var body: some View {
textField.background(editing ? Color.green : Color.red)
}
}
Use this custom text field elsewhere in your code, as you want
推荐阅读
- apache - 将 Jira 服务器迁移到 https - 小工具和应用搜索不再起作用
- excel - 通过 VBA 加快在多个工作表中查找出现的速度
- encryption-asymmetric - 具有多个私钥的单个公钥:可能/可行?
- android - 在 Firebase crashlytics 中看不到非致命错误
- node.js - 节点js REST API
- apache-kafka - Kafka Connect:一个接收器连接器,用于从一个主题写入多个表
- android - RecyclerView 仅显示前 2 个项目
- python - 如何使用 Flask Babel 翻译元组列表中的值?
- azure - 搜索时天蓝色搜索的认知能力是什么?
- android - 如何检测 MenuItemSprite (Cocos2dx 4.0) 中的双击?