繁体   English   中英

如何在不中断响应者链的情况下将UIViewController的视图嵌入另一个视图

[英]How to embed a UIViewController's view into another view without breaking responder chain

我正在尝试为View Controllers实现一个基类,该基类将添加一个基于特定状态显示和隐藏的顶部横幅。 因为我的UIViewController扩展了此基类,所以在必须将原始view属性嵌入以编程方式创建的新view属性中时, view属性已经加载。

嵌入效果很好(UI方面),视图可以适当地进行自我调整,但是我看到的问题是,嵌入视图之后,嵌入的视图不再响应触摸事件,在我最初嵌入的控制器中,我得到了一个带有16行和一个按钮的表格视图,在嵌入之前,它们都可以正确响应点击和滚动事件。

我受制于以下事实:我无法在IB中使用拆分视图控制器来实现双重视图拆分。

这是我当前的实现,似乎无法弄清事件传播的缺失,我尝试使用一个自定义视图,该视图覆盖hitTest() ,对于newRootViewcontentView变量均无济于事。

非常感谢任何帮助或见解,谢谢!

class BaseViewController: UIViewController {
    var isInOfflineMode: Bool {
       didSet { if isInOfflineMode { embedControllerView() }}
    }

    var offlineBanner: UIView!
    var contentView: UIView!

    private func embedControllerView() {
        guard let currentRoot = viewIfLoaded else { return }

        currentRoot.translatesAutoresizingMaskIntoConstraints = false

        let newRootView = UIView(frame: UIScreen.main.bounds)
        newRootView.backgroundColor = .yellow // debugging
        newRootView.isUserInteractionEnabled = true
        newRootView.clipsToBounds = true
        view = newRootView

        offlineBanner = createOfflineBanner() // returns a button that I've verified to be tapable.

        view.addSubview(offlineBanner)

        contentView = UIView(frame: .zero)
        contentView.backgroundColor = .cyan // debugging
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentView.isUserInteractionEnabled = true
        view.addSubview(contentView)

        contentView.addSubview(currentRoot)

        let bannerHeight: CGFloat = 40.00
        var topAnchor: NSLayoutYAxisAnchor
        var bottomAnchor: NSLayoutYAxisAnchor
        var trailingAnchor: NSLayoutXAxisAnchor
        var leadingAnchor: NSLayoutXAxisAnchor

        if #available(iOS 11.0, *) {
            topAnchor = view.safeAreaLayoutGuide.topAnchor
            bottomAnchor = view.safeAreaLayoutGuide.bottomAnchor
            leadingAnchor = view.safeAreaLayoutGuide.leadingAnchor
            trailingAnchor = view.safeAreaLayoutGuide.trailingAnchor
        } else {
            topAnchor = view.topAnchor
            bottomAnchor = view.bottomAnchor
            leadingAnchor = view.leadingAnchor
            trailingAnchor = view.trailingAnchor
        }

        NSLayoutConstraint.activate([
            offlineBanner.heightAnchor.constraint(equalToConstant: bannerHeight),
            offlineBanner.topAnchor.constraint(equalTo: topAnchor),
            offlineBanner.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
            offlineBanner.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
        ])

        NSLayoutConstraint.activate([
            contentView.topAnchor.constraint(equalTo: topAnchor, constant: bannerHeight),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0),
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
        ])

        OfflineViewController.migrateViewContraints(from: currentRoot, to: contentView)

        view.setNeedsUpdateConstraints()
        offlineBanner.setNeedsUpdateConstraints()
        currentRoot.setNeedsUpdateConstraints()
    }

    private func unembedControllerView() {
        let v = contentView.subviews[0]
        v.removeFromSuperview()
        view = v
        OfflineViewController.migrateViewContraints(from: contentView, to: v)
    }

    /**
     Replaces any constraints associated with the current root's safe area`UILayoutGuide` or with the actual
     current root view.
     */
    private static func migrateViewContraints(from currentRoot: UIView, to newRoot: UIView) {
        for ct in currentRoot.constraints {
            var firstItem: Any? = ct.firstItem
            var secondItem: Any? = ct.secondItem

            if #available(iOS 11.0, *) {
                if firstItem as? UILayoutGuide == currentRoot.safeAreaLayoutGuide {
                    debugPrint("Migrating firstItem is currentLayoutGuide")
                    firstItem = newRoot.safeAreaLayoutGuide
                }

                if secondItem as? UILayoutGuide == currentRoot.safeAreaLayoutGuide {
                    debugPrint("Migrating secondItem is currentLayoutGuide")
                    secondItem = newRoot.safeAreaLayoutGuide
                }
            }

            if firstItem as? UIView == currentRoot {
                debugPrint("Migrating firstItem is currentRoot")
                firstItem = newRoot
            }

            if secondItem as? UIView == currentRoot {
                debugPrint("Migrating secondItem is currentRoot")
                secondItem = newRoot
            }

            NSLayoutConstraint.deactivate([ct])
            NSLayoutConstraint.activate([
                NSLayoutConstraint(item: firstItem as Any,
                                   attribute: ct.firstAttribute,
                                   relatedBy: ct.relation,
                                   toItem: secondItem,
                                   attribute: ct.secondAttribute,
                                   multiplier: ct.multiplier,
                                   constant: ct.constant)
            ])
        }
    }
}

在此特定视图中,绿色按钮确实获取事件,这是我以编程方式创建的按钮:

嵌入工作

这是不响应事件的视图,一个带有按钮的表视图:

嵌入无效

我知道了问题所在; 我在contentView (新的根视图)和原始的self.view之间缺少约束。 这些约束实质上使原始的currentRoot视图占据了contentView的全部空间:

NSLayoutConstraint.activate([
  currentRoot.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
  currentRoot.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
  currentRoot.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
  currentRoot.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
])

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM