SwiftUI MVVM架构下横屏转竖屏适配异常问题求助
iOS 14.0 UIViewRepresentable TextField横竖屏适配问题(MVVM架构下)
问题原因
绑定@State变量时,横竖屏切换会触发SwiftUI视图的body重建,间接促使UIViewRepresentable更新其封装的UIKit TextField布局。但使用ViewModel的@Published属性时,只有当绑定的文本值变化才会调用updateUIView,横竖屏切换时文本未改变,导致UIKit TextField未触发重新布局,宽度停留在横屏状态。
解决方案
方案1:在updateUIView中强制触发布局更新
修改你的UIViewRepresentable实现,确保每次updateUIView被调用时(包括视图环境变化),强制UIKit TextField重新布局:
struct NormalSkyFloatingWithIconTextField: UIViewRepresentable { @Binding var text: String let placeHolder: String let imageName: String // 其他参数... func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.placeholder = placeHolder textField.translatesAutoresizingMaskIntoConstraints = false // 配置图标、样式等 // 设置内容优先级,确保TextField跟随父视图宽度 textField.setContentHuggingPriority(.defaultLow, for: .horizontal) textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) return textField } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text // 强制触发布局更新 uiView.setNeedsLayout() uiView.layoutIfNeeded() } // 让SwiftUI正确计算视图尺寸 func sizeThatFits(_ proposal: ProposedViewSize, uiView: UITextField, context: Context) -> CGSize? { uiView.sizeThatFits(proposal.replacingUnspecifiedDimensions()) } }
方案2:监听横竖屏切换通知,强制更新布局
在UIViewRepresentable中监听设备方向变化通知,直接触发TextField的布局更新:
struct NormalSkyFloatingWithIconTextField: UIViewRepresentable { @Binding var text: String let placeHolder: String let imageName: String // 其他参数... func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.placeholder = placeHolder textField.translatesAutoresizingMaskIntoConstraints = false // 配置其他属性 // 监听方向变化,触发布局 NotificationCenter.default.addObserver( textField, selector: #selector(UITextField.layoutIfNeeded), name: UIDevice.orientationDidChangeNotification, object: nil ) return textField } func dismantleUIView(_ uiView: UITextField, coordinator: Coordinator) { // 移除通知监听,避免内存泄漏 NotificationCenter.default.removeObserver(uiView) } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text } }
方案3:通过ViewModel触发视图更新
如果需要保持MVVM逻辑的一致性,可以在ViewModel中添加一个触发布局的标记,横竖屏切换时切换该标记,促使SwiftUI视图更新:
ViewModel修改:
class SignInViewModel: ObservableObject { @Published var emailAddress: String = "" @Published var password: String = "" @Published private var layoutTrigger = false func triggerLayoutUpdate() { layoutTrigger.toggle() } }
视图修改:
struct SignInView: View { @StateObject var viewModel = SignInViewModel() var body: some View { VStack(spacing: 15) { NormalSkyFloatingWithIconTextField( text: $viewModel.emailAddress, placeHolder: "Email Address*", imageName: "EmailIcon", characterRangeDelegate: self, textFieldValue: TextFieldValues.EmailAddress ) .frame(height: 45) NormalSkyFloatingTextField_Secure( text: $viewModel.password, placeHolder: "Password*", characterRangeDelegate: self, textFieldValue: TextFieldValues.PassWord, isSecureTextEntry: viewModel.isPasswordVisible ) .frame(height: 45) } .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in viewModel.triggerLayoutUpdate() } } }
关键注意点
- 确保UIKit TextField没有设置固定宽度,必须使用自动布局约束(leading/trailing绑定父视图,或设置内容优先级)。
- iOS 14中
@StateObject是稳定的,但横竖屏切换不会自动触发ViewModel的@Published更新,因此需要手动触发视图布局。
内容的提问来源于stack exchange,提问作者Murali Karthick
相关产品推荐
相关产品推荐

