UITableView单元格中带matchedGeometryEffect的SwiftUI视图动画跳变问题
UITableView单元格中SwiftUI视图带
matchedGeometryEffect的动画跳变问题 问题说明
- 已制作最小可复现示例(MRE),用于展示UITableView单元格内,带有
matchedGeometryEffect的SwiftUI视图在高度更新时,动画过程存在视觉跳变的问题。 - 最初单元格完全无法调整高度,即便将
UIHostingController的sizingOptions设置为intrinsicContentSize或preferredContentSize也无效。 - 为解决高度不更新的问题,我在视图中添加了
onSizeChange闭包,现在单元格高度能正常调整,但动画启动时会出现不符合动画曲线的奇怪跳变。 - 当前限制:暂时无法使用新的
UIHostingConfiguration单元格API(曾尝试使用,但出现未调试的空白单元格,若该方案可行则会考虑)。
最小可复现代码
import SwiftUI import UIKit import PlaygroundSupport // MARK: - SwiftUI View struct RootView: View { // 视图尺寸变化时触发的回调 var onSizeChange: ((CGSize) -> Void)? = nil @State private var isExpanded = false @Namespace var namespace var body: some View { Group { if isExpanded { subview1 Text("d") Text("e") Text("f") Text("g") } else { Text("a") Text("b") Text("c") subview1 } } // 通过GeometryReader检测尺寸变化并触发回调 .background(GeometryReader { geo in Color.clear .onAppear { onSizeChange?(geo.size) } .onChange(of: geo.size) { newSize in onSizeChange?(newSize) } }) .onTapGesture { isExpanded.toggle() } .background(Color.green) .animation(.easeInOut, value: isExpanded) } var subview1: some View { Text("animatable view") .border(Color.red) .matchedGeometryEffect(id: "1", in: namespace) .id("1") } } // MARK: - 自定义Table View Cell class HostingTableViewCell: UITableViewCell { // 持有UIHostingController的引用 var host: UIHostingController<RootView>? func configure() { // 创建带尺寸变化回调的hosting控制器 let host = UIHostingController(rootView: RootView(onSizeChange: { [weak self] newSize in guard let self = self else { return } // 失效固有内容尺寸 self.host?.view.invalidateIntrinsicContentSize() // 强制表格视图更新布局 if let tableView = self.superview as? UITableView ?? self.superview?.superview as? UITableView { tableView.beginUpdates() tableView.endUpdates() } })) self.host = host host.sizingOptions = .intrinsicContentSize host.view.translatesAutoresizingMaskIntoConstraints = false host.view.backgroundColor = .blue contentView.addSubview(host.view) NSLayoutConstraint.activate([ host.view.topAnchor.constraint(equalTo: contentView.topAnchor), host.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), host.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), host.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor) ]) } } // MARK: - 宿主视图控制器 class HostViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white tableView = UITableView() tableView.translatesAutoresizingMaskIntoConstraints = false tableView.delegate = self tableView.dataSource = self tableView.allowsSelection = false tableView.register(HostingTableViewCell.self, forCellReuseIdentifier: "cell") view.addSubview(tableView) NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: view.topAnchor), tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor) ]) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HostingTableViewCell cell.configure() return cell } } // MARK: - 启动Playground // PlaygroundPage.current.liveView = UIHostingController(rootView: RootView()) PlaygroundPage.current.liveView = HostViewController() PlaygroundPage.current.needsIndefiniteExecution = true
内容的提问来源于stack exchange,提问作者fingia
相关产品推荐
相关产品推荐

