SwiftUI多TextField渲染卡顿问题求助,附复现代码示例
SwiftUI 多TextField卡顿及List不渲染问题解决方案
一、TextField渲染卡顿优化方案
- 减少不必要的视图重绘
你的demo里ContactCard的isTextField是@Binding,每次切换全局isTextField时所有ContactCard都会触发重绘,而且你的emailAddresses是计算属性,每次重绘都会重新生成数组,加剧资源消耗。可以把ContactCard标记为Equatable,让SwiftUI只在必要的时候重绘:
给ContactCard加扩展:
然后在使用extension ContactCard: Equatable { static func == (lhs: ContactCard, rhs: ContactCard) -> Bool { lhs.name.last == rhs.name.last && lhs.isTextField == rhs.isTextField } }ContactCard的地方加.equatable()修饰符:ContactCard(name: contact, isTextField: $isTextField) .equatable() - 启用懒加载机制
你现在用的ScrollView+VStack会一次性渲染所有20*3=60个TextField,没有复用机制。改用LazyVStack替代普通VStack,只会渲染屏幕可视区域内的视图,超出屏幕的部分滚动时才渲染,实测能把首次渲染耗时降到和Text几乎持平:ScrollView { LazyVStack { // 替换原来的VStack // 原有内容保持不变 } } - 绑定逻辑优化
实际业务中不要直接在ForEach里生成绑定,提前把数据源做成@State或者@ObservedObject管理的结构体数组,每个元素对应一个TextField的绑定值,避免每次重绘时重新生成绑定。 - 样式逻辑裁剪
不要给TextField加多余的修饰符,很多全局的修饰符(比如自定义字体、边框)如果是写在ForEach内部,会每个TextField单独计算样式,统一提到父视图或者用自定义TextField封装一次即可。
二、List不渲染问题解决方案
你遇到的List不渲染是因为你的ForEach的id参数用了\.self.last,而List内部的复用机制要求id必须全局唯一,你的demo里如果有重姓的联系人就会出现复用冲突,甚至完全不渲染,修改方案:
- 首先把联系人数据改成遵从
Identifiable协议的结构体,自带唯一id:
struct Contact: Identifiable { let id = UUID() let first: String let last: String }
- 替换原来的contacts数组为
[Contact]类型,ForEach直接不用写id参数:
ForEach(contacts) { contact in ContactCard(contact: contact, isTextField: $isTextField) .equatable() }
- 如果还要保留元组写法,就把id改成更唯一的标识,比如用first+last拼接的字符串:
ForEach(contacts, id: \.self.first + \.self.last) { contact in // 原有内容保持不变 }
改完之后List就能正常渲染所有视图了,而且List自带单元格复用机制,渲染大量TextField的性能比普通ScrollView还要好。
内容的提问来源于stack exchange,提问作者Mike R
相关产品推荐
相关产品推荐

