当我添加一个包含 SwiftUI 视图作为 childView 的 UIHostingController,然后将该 childView 放入 UIScrollView 中时,滚动会中断。
我有我的看法
struct TestHeightView: View {
let color: UIColor
var body: some View {
VStack {
Text("THIS IS MY TEST")
.frame(height: 90)
}
.fixedSize(horizontal: false, vertical: true)
.background(Color(color))
.edgesIgnoringSafeArea(.all)
}
}
然后我有一个 UIViewController 和一个 UIScrollView 作为子视图。 UIScrollView 内部有一个 UIStackView,它被正确设置为允许加载 UIView 并在堆栈高度变得足够大时滚动它们。这有效。如果我要加载 40 个 UILabels,它会完美地滚动它们。
当我添加一个普通的旧 UIView,然后在该容器内添加一个 UIHostingController 时,问题就出现了。我这样做是这样的:
let container = UIView()
container.backgroundColor = color.0
stackView.insertArrangedSubview(container, at: 0)
let test = TestHeightView(color: color.1)
let vc = UIHostingController(rootView: test)
vc.view.backgroundColor = .clear
add(child: vc, in: container)
func add(child: UIViewController, in container: UIView) {
addChild(child)
container.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
child.view.topAnchor.constraint(equalTo: container.topAnchor, constant: 0).isActive = true
child.view.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: 0).isActive = true
child.view.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
child.view.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: 0).isActive = true
child.didMove(toParent: self)
}
在我的示例中,我添加了 3 个这样的 containerViews/UIHostingController,然后添加了一个 UIView(绿色)来演示正在发生的情况。
您可以看到,当我滚动时,所有视图都会因形成间隙而暂停。发生的情况是包含的 UIView(浅色)正在扩展其高度。一旦高度达到某个值,滚动就会照常继续,直到下一个容器/UIHostingController 到达顶部并再次开始。
我研究了几种不同的解决方案 .edgesIgnoringSafeArea(.all)
确实做了某事。我将其包含在我的示例中,因为如果没有它,问题完全相同,只是更加刺耳并且更难以使用视频来解释。基本上同样的事情发生,但没有任何动画,它只是看起来 UIScrollView 停止工作,然后它再次工作
编辑:
我添加了另一个 UIViewController 只是为了确保它不是一般的儿童引起的问题。没有。只有 UIHostingController 可以执行此操作。 SwiftUI 中的一些东西
令人难以置信的是,这是我能想到的唯一答案:
我在 Twitter 上找到了它https://twitter.com/b3ll/status/1193747288302075906?s=20作者:Adam Bell
class EMHostingController<Content> : UIHostingController<Content> where Content : View {
func fixedSafeAreaInsets() {
guard let _class = view?.classForCoder else { return }
let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { (sself : AnyObject!) -> UIEdgeInsets in
return .zero
}
guard let method = class_getInstanceMethod(_class.self, #selector(getter: UIView.safeAreaInsets)) else { return }
class_replaceMethod(_class, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method))
let safeAreaLayoutGuide: @convention(block) (AnyObject) ->UILayoutGuide? = { (sself: AnyObject!) -> UILayoutGuide? in
return nil
}
guard let method2 = class_getInstanceMethod(_class.self, #selector(getter: UIView.safeAreaLayoutGuide)) else { return }
class_replaceMethod(_class, #selector(getter: UIView.safeAreaLayoutGuide), imp_implementationWithBlock(safeAreaLayoutGuide), method_getTypeEncoding(method2))
}
override var prefersStatusBarHidden: Bool {
return true
}
}
最近遇到了同样的问题,还确认安全区域插入破坏了滚动。我使用
ignoresSafeArea
修饰符修复了 iOS 14+:
public var body: some View {
if #available(iOS 14.0, *) {
contentView
.ignoresSafeArea()
} else {
contentView
}
}
我遇到了非常相似的问题,并通过将以下内容添加到我的
UIHostingController
子类中找到了解决方案:
override func viewDidLoad() {
super.viewDidLoad()
edgesForExtendedLayout = []
}
我相信这个问题已在 iOS 16.4 中通过新的
safeAreaRegions
属性 UIHostingController
得到解决。它的默认值是 .all
,这会导致 SwiftUI 在触摸安全区域时发生变化。所以解决方法如下所示:
let vc = UIHostingController(rootView: YOUR_VIEW)
vc.safeAreaRegions = []