代码之家  ›  专栏  ›  技术社区  ›  Sti

当高度改变时,UINavigationController会关闭内容?(gif格式)

  •  0
  • Sti  · 技术社区  · 5 年前

    我已将一个UINavigationController放入一个可扩展的“抽屉”中。 我的目标是让导航堆栈中的每个viewController都有自己的“首选”高度。

    假设VC1需要很高。从VC2导航回VC1时,我希望它的高度设置为高。动画逻辑似乎是可行的,即使有刷卡的交互作用。

    但是由于某种原因,导航控制器中的viewControllers被“切断”。它们的约束是正确的,但没有更新(?)。或者部分内容在我再次触摸视图之前无法呈现。底部看不见的区域甚至可以接受触摸。

    看一看:

    gif

    预期的结果是contentViewControllers(第一个和第二个)总是扩展到屏幕的底部。它们是这样做的约束,所以问题是它们不会“呈现”(?)在过渡时期。

    在UINavigationController的代理中,我执行以下操作:

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        transitionCoordinator?.animate(alongsideTransition: { (context) in
                drawer.heightConstraint.constant = targetHeight
                drawer.superview?.layoutIfNeeded()
            }, completion: { (context) in
                print("done")
            })
    }
    

    高度变化是完美的。但导航中的内容不符合要求。navigationController被约束为leading、trailing、bottom和更改其常量的存储heightConstraint。

    只要我触摸/拖动导航控制器/内容,它就会立即“呈现未渲染的”,一切都很好。为什么会这样?

    检查视图层次结构时,如下所示:

    enter image description here

    导航控制器的高度与需要的高度相同,但内容的高度与转换开始时整个抽屉的高度相同,在我触摸它之前它不会更新。 为什么?

    编辑:我已经把代码推到 my GitHub 如果你想看看。不过要注意,还有其他几个问题(动画等),不要介意。我只想知道为什么导航不会呈现“未来”的高度。

    0 回复  |  直到 5 年前
        1
  •  2
  •   Mojtaba Hosseini    5 年前

    您可以自己更新控制器高度:

    首先,您需要保留对控制器的引用:

    class ContainedNavigationController: UINavigationController, Contained, UINavigationControllerDelegate {
    
        private var controllers = [UIViewController]()
        func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { controllers = viewControllers }
    
    /* Rest of the class */
    

    然后你可以相应地更新它们的高度。 别忘了添加新控制器 .

        private func updateHeights(to height: CGFloat, willShow controller: UIViewController) {
            controller.view.frame.size.height = height
            _ = controllers.map { $0.view.frame.size.height = height }
        }
    

    您可以在代码中这样使用它:

        func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    
            if let superHeight = view.superview?.superview?.bounds.size.height, let drawer = (view.superview as? Drawer), let targetHeight = (viewController as? Contained)?.currentNotch.height(availableHeight: superHeight){
    
                transitionCoordinator?.animate(alongsideTransition: { (context) in
                    drawer.heightConstraint.constant = targetHeight
                    drawer.superview?.layoutIfNeeded()
    
                    self.updateHeights(to: targetHeight, willShow: viewController) // <- Here
                }, completion: { (context) in
                    self.updateHeights(to: targetHeight, willShow: viewController) // <- Here
                })
            }
        }
    

    结果:

    Demo

    也许这不是最干净的代码,但我只想解决这个问题并给你一个想法


    更新

    当你从边缘拖动时,你所看到的阴影是一个叫做 UIParallaxDimmingView . 我也给那个尺寸加了一个补丁。所以不再有视觉问题:

        private func updateHeights(to height: CGFloat, willShow controller: UIViewController) {
            controller.view.frame.size.height = height
            _ = controllers.map { $0.view.frame.size.height = height }
    
            guard controllers.contains(controller) else { return }
            _ = controller.view.superview?.superview?.subviews.map {
                guard "_UIParallaxDimmingView" == String(describing: type(of: $0)) else { return }
                $0.frame.size.height = height
            }
        }
    

    我添加了一个请求 my fork .

        2
  •  3
  •   Subramanian Mariappan    5 年前

    您可以通过为导航控制器设置自定义的animationController并为中的destinationView设置适当的帧来解决上述问题 动画转换 你的习惯功能 uiviewcontrolleranimated转换 实施。结果将与下面gif图像中的结果类似。

    enter image description here

    您的自定义UIViewControllerAnimatedTransitioning可能如下所示。

    final class TransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
        let presenting: Bool
    
        init(presenting: Bool) {
            self.presenting = presenting
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return TimeInterval(UINavigationController.hideShowBarDuration)
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            guard let fromView = transitionContext.view(forKey: .from) else { return }
            guard let toView = transitionContext.view(forKey: .to) else { return }
    
            let duration = transitionDuration(using: transitionContext)
    
            let container = transitionContext.containerView
            if presenting {
                container.addSubview(toView)
            } else {
                container.insertSubview(toView, belowSubview: fromView)
            }
    
            let toViewFrame = toView.frame
            toView.frame = CGRect(x: presenting ? toView.frame.width : -toView.frame.width, y: toView.frame.origin.y, width: toView.frame.width, height: toView.frame.height)
    
            UIView.animate(withDuration: duration, animations: {
                toView.frame = toViewFrame
                fromView.frame = CGRect(x: self.presenting ? -fromView.frame.width : fromView.frame.width, y: fromView.frame.origin.y, width: fromView.frame.width, height: fromView.frame.height)
            }) { (finished) in
                container.addSubview(toView)
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
        }
    }
    

    并且,在导航控制器中提供自定义的animationController,如下所示。

    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
       switch operation {
       case .push:
          return TransitionAnimator(presenting: true)
       case .pop:
          return TransitionAnimator(presenting: false)
       default:
          return nil
       }
    }
    

    注:我在你的github回购协议中也给出了一个拉取请求。

        3
  •  0
  •   SPatel mash    5 年前

    您可以设置如下尺寸: 参考-> https://github.com/satishVekariya/Drawer/tree/boom-diggy-boom

    class FirstViewController: UIViewController, Contained {
       // your code 
    
       override func updateViewConstraints() {
           super.updateViewConstraints()
           if let parentView = parent?.view {
              view.frame.size.height = parentView.frame.height
           }
       }   
    }
    

    你的导航vc:

    class ContainedNavigationController:UINavigationController, Contained, UINavigationControllerDelegate{
       ///Your code
    
       func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
           viewController.updateViewConstraints()
               // Your code start
               // end
       }
    
       func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
           viewController.updateViewConstraints()
       }
    
    }
    
        4
  •  0
  •   khetaram kumawat Likmaram    5 年前

    可以设置高度约束并调用layoutIfNeeded方法,而不使用转换块。

    下面是相同的代码段:

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    
        if let superHeight = view.superview?.superview?.bounds.size.height, let drawer = (view.superview as? Drawer), let targetHeight = (viewController as? Contained)?.currentNotch.height(availableHeight: superHeight){
            drawer.heightConstraint.constant = targetHeight
            drawer.layoutIfNeeded()
        }
    }
    

    @如果有帮助请告诉我,请投赞成票