父子控制器

作者: 明月钓无痕 | 来源:发表于2016-01-28 11:47 被阅读319次
    • 什么是父子控制器
      如果对于这个词熟悉的话.那个我们看一下 UIViewController,他里面有一个属性
    public var childViewControllers: [UIViewController] { get }
    

    还有一个方法

    // 添加子控制器
    public func addChildViewController(childController: UIViewController)
    // 从父控制器中移除
    public func removeFromParentViewController()
    

    看到这写.是否大概知道下面要说的是什么了.
    尤其是我们在使用一个UITabBarController.可以很明显的感觉到.

    • 为什么使用父子控制器
      在实际应用中, 如果只是将view添加到控制器中的话,而不是将控制器设置为父子关系话.那么有些消息传递,则无法传递到子控制器.比如说控制器的转屏,如果只是普通的添加,那么转屏的事件将无法传递给one控制器. 还有其他一些消息事件.
        func click(btn: UIButton) {
            let one = OneTableViewController()
            one.view.frame = CGRect(x: 0, y: 64, width: self.view.frame.size.width, height: self.view.frame.size.height - 64)
            self.one = one
            self.view.addSubview(one.view)
        }
    

    所以我们最好是使用添加子控制器

    addChildViewController(OneTableViewController())
    
    • 什么时候使用父子控制器
      如果当一个界面足够复杂,使用一个控制器进行管理的话,控制器会显得格外臃肿,这样的话,我们就有必要将View分区,然后分配到其他控制器.这样会使代码的逻辑性更强.增强可读性.简化单个控制器.

    为了更方便阅读,下面贴一下控制器的代码

    class ViewController: UIViewController {
        
        // 正在显示的控制器
        var showingVC: UIViewController?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let view = UIView(frame: CGRect(x: 0, y: 20, width: self.view.frame.size.width, height: 44))
            
            for i in 0..<3 {
                let btn = UIButton(frame: CGRect(x: view.frame.size.width / 3.0 * CGFloat(i), y: 0, width: view.bounds.width / 3.0, height: view.bounds.height))
                btn.backgroundColor = UIColor.grayColor()
                btn.setTitle("btn\(i)", forState: .Normal)
                btn.addTarget(self, action: "click:", forControlEvents: .TouchUpInside)
                view.addSubview(btn)
            }
            self.view.addSubview(view)
            
            // 通过addChildViewController,添加的控制器都会加载到childViewControllers这个数组里
            addChildViewController(OneTableViewController())
            addChildViewController(TwoViewController())
            addChildViewController(ThreeViewController())
        }
    
        func click(btn: UIButton) {
            
            // 先移除其他控制器的View,这样才能保证只有一个View被显示,而不是叠加在一起
            showingVC?.view.removeFromSuperview()
            
            // 获取索引
            let index = btn.superview?.subviews.indexOf(btn)
            
            // 改变当前控制器,并将view添加到主控制器
            showingVC = childViewControllers[index!]
            showingVC!.view.frame = CGRect(x: 0, y: 64, width: self.view.frame.size.width, height: self.view.frame.size.height - 64)
            self.view.addSubview((showingVC?.view)!)
        }
    }
    
    // 屏幕即将旋转时调用这个方法
    extension ViewController {
        override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
            debugPrint("\(self.classForCoder)willRotateToInterfaceOrientation")
        }
    }
    

    为了说明父子控制器的重要性,我们在上面的基础上再演示一下其他的效果.
    我在点击two控制器时推出一个测试控制器

        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            let testVC = TestViewController()
            presentViewController(testVC, animated: true, completion: nil)
        }
    

    假如说,test控制器很复杂,我们添加了一个test2控制器的View.我需要的是点击test2视图的时候dismiss掉该控制器.先看看不是父子控制器会出现什么情况.

    class TestViewController: UIViewController {
    
        var test2: Test2ViewController?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.blueColor()
            let test2 = Test2ViewController()
            test2.view.frame = CGRect(x: 10, y: 10, width: 100, height: 100)
            self.test2 = test2
            view.addSubview(test2.view)
        }
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            // 为了测试是否调用了该方法
            debugPrint("TestViewController")
            dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    同样我们在test2控制器里添加这个方法

        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            debugPrint("Test2ViewController")
            dismissViewControllerAnimated(true, completion: nil)
        }
    

    当我们点击test2的View时下面我将测试结果,虽然控制台打印了Test2ViewController这句话,但是控制器并没有dismiss掉,很明显,因为test2不是present出来的.

    但是当我们改一下

    class TestViewController: UIViewController {
    
    //    var test2: Test2ViewController?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.blueColor()
            let test2 = Test2ViewController()
            test2.view.frame = CGRect(x: 10, y: 10, width: 100, height: 100)
    //        self.test2 = test2
            view.addSubview(test2.view)
            addChildViewController(test2)
        }
    
     
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            
            debugPrint("TestViewController")
            dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    只是改成了父子关系,这样的话已经,点击test2区域,还是打印Test2ViewController,不同的是这是控制器被dismiss掉了.也就是说,由于父控制器是present出来的,子控制器由此得到了事件消息.这样才能确保事件在传递过程中,不会由于层级关系的问题导致消息链中断.

    对于这种现象,navigation的push也同样出出现类似的问题.

    测试代码:https://github.com/WANGYUE0707/ChildController

    相关文章

      网友评论

        本文标题:父子控制器

        本文链接:https://www.haomeiwen.com/subject/vtadkttx.html