Swift移除bannerView后feedView视觉消失问题求助
问题
在Swift开发中遇到如下问题:在superview中移除bannerView后,feedView虽仍存在于self.view.subviews列表中,且isHidden属性为false,却在视觉上消失,仅显示白色背景。
相关代码如下:
private func setupSubviews() { ... ... self.view.addSubview(self.feedView) self.view.addSubview(self.bannerView) }
private func setupConstraints() { self.view.backgroundColor = .white self.feedView.backgroundColor = .white self.feedView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true self.bannerView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true constrain(self.feedView, self.view, self.bannerView) { feedView, container, banner in feedView.left == container.left feedView.right == container.right feedView.bottom == banner.top banner.left == container.left banner.right == container.right } for view in self.view.subviews { print("View: ", view) //self.feedView.removeFromSuperview() if view is DFPBannerView { print(self.bannerView.superview) self.bannerView.removeFromSuperview() } } for view in self.view.subviews { print("&&&***", view) // 打印显示feedView仍存在 print(view.isHidden) // 显示为false } }
在setupSubviews方法中为self.view添加了feedView和bannerView两个子视图,设置约束时将feedView的bottom锚点与bannerView的top锚点绑定。执行self.bannerView.removeFromSuperview()后,feedView就不再显示;但如果移除feedView,bannerView却能正常显示。尝试设置self.feedView.isHidden = false也无效果,想请教这其中的原因是什么?
原因分析与解决办法
核心原因
feedView的布局完全依赖已被移除的bannerView——你设置了feedView.bottom == banner.top的约束,当bannerView被从父视图移除后,这个约束会变成无效约束,Auto Layout无法正确计算feedView的底部位置,最终导致feedView的高度被压缩为0(或布局逻辑异常),所以视觉上看不到它,但它确实还在subviews列表里。
而bannerView的约束只依赖父视图的safeArea底部,即使feedView被移除,它的约束依然有效,因此能正常显示。
解决方法
先移除关联约束再删除视图
在移除bannerView之前,找到并停用feedView与bannerView之间的bottom-top约束:// 遍历父视图的约束,找到关联的约束并停用 for constraint in view.constraints { if (constraint.firstItem as? UIView == feedView && constraint.firstAttribute == .bottom) && (constraint.secondItem as? UIView == bannerView && constraint.secondAttribute == .top) { constraint.isActive = false break } } bannerView.removeFromSuperview()提前设置备用约束
初始化时给feedView创建两个底部约束,根据bannerView的存在状态切换启用:// 创建两个约束 let feedBottomToBanner = feedView.bottomAnchor.constraint(equalTo: bannerView.topAnchor) let feedBottomToSafeArea = feedView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) // 默认启用与bannerView的约束 feedBottomToBanner.isActive = true // 移除bannerView时切换约束 feedBottomToBanner.isActive = false feedBottomToSafeArea.isActive = true bannerView.removeFromSuperview()利用约束优先级
给feedView的底部设置一个低优先级的备用约束,确保bannerView移除后,Auto Layout会自动采用这个约束:let feedBottomToBanner = feedView.bottomAnchor.constraint(equalTo: bannerView.topAnchor) let feedBottomToSafeArea = feedView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) // 设置备用约束优先级低于主约束 feedBottomToSafeArea.priority = .defaultHigh feedBottomToBanner.isActive = true feedBottomToSafeArea.isActive = true
内容的提问来源于stack exchange,提问作者Mark

