You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 17中SwiftUI TextField聚焦状态下Setter无法正常工作

iOS 17 SwiftUI TextField聚焦时内联Setter值无法即时更新的解决方案

问题说明

  • 在iOS 17.0中,处于聚焦状态的SwiftUI TextField使用自定义get/set绑定逻辑时,setter中修改的状态值无法立刻同步到TextField界面,必须等TextField失去焦点才会更新显示。
  • 该问题在iOS 17模拟器和物理设备上均会出现,但iOS 15、iOS 16版本可正常运行。

问题复现代码

struct ContentView: View{
    @State var cardNumber: String = ""
    var body: some View{
            TextField("", text: .init(get: {
                cardNumber
            }, set: { value in
                cardNumber = ""
                let startIndex = value.startIndex
                for index in 0..<value.count{
                    let stringIndex = value.index(startIndex, offsetBy: index)
                    cardNumber += String(value[stringIndex])
                    if (index + 1) % 5 == 0 && value[stringIndex] != " "{
                        cardNumber.insert(" ", at: stringIndex)
                    }
                }
                cardNumber = String(cardNumber.prefix(23))  
            })).background(Color.yellow) 
    } 
}

原因分析

iOS 17对SwiftUI TextField的绑定机制做了调整:当TextField处于聚焦编辑状态时,会优先维护内部的输入缓存,此时直接在自定义setter中修改@State变量,SwiftUI不会立即触发视图刷新,导致界面显示与状态值不同步。

解决方案

方案1:改用ObservableObject触发可靠更新

使用@Published结合ObservableObject,利用Combine的发布机制强制触发视图更新,同时优化原有的卡号格式化逻辑(避免原代码索引操作可能导致的崩溃):

class CardViewModel: ObservableObject {
    @Published var cardNumber: String = ""
}

struct ContentView: View{
    @StateObject private var viewModel = CardViewModel()
    
    var body: some View{
        TextField("", text: .init(get: {
            viewModel.cardNumber
        }, set: { value in
            var formattedNumber = ""
            // 先过滤掉所有空格,避免重复处理
            let filteredValue = value.replacingOccurrences(of: " ", with: "")
            
            for (index, char) in filteredValue.enumerated() {
                formattedNumber.append(char)
                // 每4位添加空格,最后一位不加
                if (index + 1) % 4 == 0 && index != filteredValue.count - 1 {
                    formattedNumber.append(" ")
                }
            }
            
            viewModel.cardNumber = String(formattedNumber.prefix(23))
        }))
        .background(Color.yellow)
    } 
}

方案2:手动延迟触发状态更新

如果想保留@State的使用方式,可以通过DispatchQueue.main.async将状态更新放到下一个主循环,强制触发视图刷新:

struct ContentView: View{
    @State var cardNumber: String = ""
    
    var body: some View{
        TextField("", text: .init(get: {
            cardNumber
        }, set: { value in
            var formattedNumber = ""
            let filteredValue = value.replacingOccurrences(of: " ", with: "")
            
            for (index, char) in filteredValue.enumerated() {
                formattedNumber.append(char)
                if (index + 1) % 4 == 0 && index != filteredValue.count - 1 {
                    formattedNumber.append(" ")
                }
            }
            
            // 延迟更新,触发视图刷新
            DispatchQueue.main.async {
                cardNumber = String(formattedNumber.prefix(23))
            }
        }))
        .background(Color.yellow)
    } 
}

方案对比

  • 方案1更符合SwiftUI的MVVM架构规范,状态更新的可靠性更高,推荐作为长期解决方案。
  • 方案2是快速适配现有代码的临时修复方式,适合小范围调整场景。

内容的提问来源于stack exchange,提问作者kishore chandru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:00:12