Xcode13及iOS15/iPadOS15下SwiftUI Form单元格高度异常问题咨询
问题描述
此前在Xcode 12、iOS 14环境下开发Form表单功能,所有表现均符合预期。切换到Xcode 13、iOS 15及iPadOS 15环境后,出现以下异常:
- 单元格高度不一致,滚动出屏幕外再返回时高度会发生变化,表现和可复用UITableViewCell一致
- 部分Section单元格高度为0,但内部文本输入框悬浮在其他单元格上方
- Picker视图设置
.pickerStyle(InlinePickerStyle())后,单元格会被拉伸至超出iPad屏幕尺寸,无法像旧版本一样显示为滚轮样式
相关代码
表单视图代码
struct EvalViewMHx: View { var body: some View { VStack { Form { Label( title: { Text("Demographics & Medical History") .font(.title) .foregroundColor(.white) .padding() }, icon: { Image(systemName: "heart.text.square.fill") .resizable() .aspectRatio(contentMode: .fit) .foregroundColor(.white) }) .listRowBackground(Color.blue) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center) Section(header: Text("Demographics")) { demographicsView(nameText: $nameText, DOB: $DOB, height: $height, weight: $weight) } Section(header: Text("Medical History")) { VStack { HStack { Text("When did you get hurt?") .padding() Spacer() Toggle("", isOn: $doesNotKnowInjuryDate) .frame(width: 35) .padding(.horizontal) Text("I don't know") .fontWeight(!doesNotKnowInjuryDate ? .regular : .bold) .padding(.leading) } if !doesNotKnowInjuryDate { DatePicker("Injury Date", selection: $injuryDate, displayedComponents: [.date]) .foregroundColor(Color(UIColor.systemGray3)) .padding() } } .frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 150, idealHeight: 150, maxHeight: 150, alignment: .center) VStack(alignment: .leading) { HStack { Text("Where is your injury?") Spacer() Text("Right") .fontWeight(isInjuryOnRightSide ? .regular : .bold) Toggle("", isOn: $isInjuryOnRightSide) .toggleStyle(SwitchToggleStyle(tint: Color(UIColor.systemGray5))) .frame(width: 35) .padding(.horizontal) Text("Left") .fontWeight(!isInjuryOnRightSide ? .regular : .bold) .padding(.leading) } .zIndex(2.0) Picker(selection: $injuryLocation, label: Text(""), content: { ForEach(injuryLocations, id: \.self) { location in Text(location) } }) .pickerStyle(WheelPickerStyle()) .frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 100, idealHeight: 100, maxHeight: 100, alignment: .center) .zIndex(1.0) .padding() } .padding() VStack(alignment: .leading) { Text("How did you get hurt? (Method of Injury)") .padding() TextEditor(text: $MOI) .frame(height: 150) .background(Color(UIColor.systemGray5).opacity(0.75)) .cornerRadius(15) } .frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 200, idealHeight: 200, maxHeight: 200, alignment: .center) .padding() if injuryLocation != "Lower Back" && injuryLocation != "Upper Back" && injuryLocation != "Head / Neck" && injuryLocation != "Chest" { VStack { pastInjuryHistory(injuryLocation: $injuryLocation, hasHadSameSideInjury: $hasHadSameSideInjury, hasHadOppositeSideInjury: $hasHadOppositeSideInjury, isSameSide: true) pastInjuryHistory(injuryLocation: $injuryLocation, hasHadSameSideInjury: $hasHadSameSideInjury, hasHadOppositeSideInjury: $hasHadOppositeSideInjury, isSameSide: false) } } } } } } }
外层TabView嵌套代码
TabView(selection: $activeTab) { EvalViewMHx() .tag(1) EvalObjectiveView() .tag(2) viewThree(activeTab: $activeTab) .tag(3) viewFour(activeTab: $activeTab) .tag(4) } .tabViewStyle(PageTabViewStyle()) .onChange(of: activeTab, perform: { value in print(activeTab) }) .animation(.default)
异常效果

解决方案
以上属于iOS 15 SwiftUI的已知兼容性问题,无需等待官方修复,可通过以下方案落地解决:
- 强制禁用Form的cell复用与自动高度估算行为,给Form添加如下修饰符:
同时给每个Section内的根容器添加.onAppear { UITableView.appearance().estimatedRowHeight = 0 UITableView.appearance().rowHeight = UITableView.automaticDimension } .onDisappear { // 页面消失时重置全局配置,避免影响其他页面 UITableView.appearance().estimatedRowHeight = UITableView.automaticDimension }.fixedSize(horizontal: false, vertical: true)、.listRowInsets(EdgeInsets())修饰符,触发布局强制计算。 - 修复Picker样式异常:给Picker添加
.labelsHidden()、.compositingGroup()、.clipped()修饰符,强制约束布局范围,示例如下:Picker(selection: $injuryLocation, label: Text("")) { ForEach(injuryLocations, id: \.self) { location in Text(location) } } .pickerStyle(.wheel) .labelsHidden() .compositingGroup() .frame(height: 100) .clipped() - 解决单元格高度为0、内容悬浮问题:移除内容容器不必要的
idealHeight/maxHeight限制,优先用minHeight配合fixedSize做自适应,同时给根容器添加.listRowBackground(Color(UIColor.systemBackground))强制触发行背景布局计算。 - 修复TabView动画冲突:把全局的
.animation(.default)修改为仅绑定activeTab变化的精准动画,避免不必要的列表重绘:.animation(.default, value: activeTab)
内容的提问来源于stack exchange,提问作者Jeff Cournoyer
相关产品推荐
相关产品推荐

