父子控制器

  • 什么是父子控制器
    如果对于这个词熟悉的话.那个我们看一下 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容