【问题标题】:SwiftUI View and UIHostingController in UIScrollView breaks scrollingUIScrollView 中的 SwiftUI View 和 UIHostingController 中断滚动
【发布时间】:2020-09-01 15:47:29
【问题描述】:

当我添加一个包含 SwiftUI 视图的 UIHostingController 作为 childView,然后将该 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 正确设置为允许加载 UIViews 并在堆栈高度变得足够大时滚动它们。这行得通。如果我要加载 40 个 UILabel,它会完美地滚动它们。

当我添加一个普通的旧 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 只是为了确保通常不是儿童导致问题。没有。只有 UIHostingControllers 这样做。 SwiftUI 中的一些东西

【问题讨论】:

  • 你会准备可重现的例子吗?提供的代码快照不足以进行测试。

标签: swift uiscrollview swiftui


【解决方案1】:

难以置信,这是我能想到的唯一答案:

我在 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
    }
}

【讨论】:

  • 这确实很令人沮丧。 FWIW,您似乎可以通过不将托管控制器添加到视图控制器层次结构来规避这些问题(但我确信这会破坏其他 UIKit 集成,可能例如路由等)
  • 感谢您分享解决方案!它解决了我的问题哈哈!您是否提出了其他解决方案,因为它看起来像一个 hacky 解决方法,我不确定它是否已准备好投入生产?
  • 我从来没有这样做过......我离开了那个项目,并决定至少在 iOS 14 之前避免使用 SwiftUI
  • 这里有一个非常好的解决方案:defagos.github.io/swiftui_collection_part3
【解决方案2】:

最近遇到了同样的问题,也确认安全区域插入打破了滚动。我使用 ignoresSafeArea 修饰符对 iOS 14+ 的修复:

    public var body: some View {
        if #available(iOS 14.0, *) {
            contentView
            .ignoresSafeArea()
        } else {
            contentView
        }
    }

【讨论】:

    猜你喜欢
    • 2020-11-12
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 2019-08-19
    相关资源
    最近更新 更多