UIContainer 2.1.1

UIContainer 2.1.1

Brenno de Moura维护。



  • brennobemoura

UIContainer

CI Status Version License Platform

安装

UIContainer可通过CocoaPods获取。要安装它,只需将以下行添加到您的Podfile中

pod 'UIContainer'

macOS支持

该库在macOS上的实际开发阶段尚未经过测试。

定义


1. UIContainer

UIContainer协议是每个容器创建的主要核心。每个UIView都可以持有另一个UIView或UIViewController。使用容器,你不必担心在屏幕上展示任何内容是否做得正确。UIContainer是一个通用的协议,可以扩展以允许其他视图成为某些内容的容器。每个容器应该从ContainerBox类型创建,但你可以创建自己的容器和容器框,没有这方面的规则。

为任何UIContainer开发的方法定义如下

  • ParentView: UIViewController
  • View: AnyObject
  • .prepareContainer(inside parentView: ParentView!, loadHandler: (() -> View?)?)
  • .removeContainer()
  • .insertContainer(view: View!)
  • .prepare(parentView: ParentView!)
  • .loadView<T: UIView>(_ view: T) -> UIView
  • .containerDidLoad()
  • .init(in parentView: ParentView!, loadHandler: (() -> View?)?)

考虑到这一点,您可以从创建容器类和使用它们开始。我们已经开发了用于UIViewControllers的容器和用于UIView的ContainerView。这里将展示一些派生类,用于Cells(尚不完全实现)和Storyboard的出口。

1.a) ContainerViewParent

这个协议很重要,因为所有容器都需要知道作为调用堆栈中叶子根的父视图控制器。

我们从顶层viewController开始。任何应用中都应该存在的第一个viewController是UIWindow.rootViewController,这是UIKit强制规定的唯一一个。所以假设根类是WindowViewController,现在所有在WindowViewController内部显示的UIView都将持有WindowViewController作为父类。

如果你有一个WindowViewController,并且有containerA<UIView>containerB<UIViewController>containerC<UIView>,这意味着containerA和containerB的父视图控制器将是WindowViewController,而containerC的父类将是containerB.UIViewController。

ContainerViewParent允许你保持对一个参考或可能父类的引用,但这并不意味着它将是正确的父类,因为有时父类是“superview”。

1.c) NibView和View

这里应定义两种视图类型。第一种是用.xib文件创建的视图,第二种是用代码创建的视图。UIKit没有为这些情况实现正确的代码,也许Apple认为这些都是不同的事情。经一番研究后,我们决定创建NibViewView。它们非常相似,但nib类型的一个通常用作其他开发者的contentView,具有@IBOutlet weak var view: UIView!。最终,生命周期是.initprepare函数。

你应该记住,这两个视图都有在实例化过程中调用的prepare函数。这意味着父类或superview没有对其的引用。

1.d) ContainerView<视图:UIView 与 ContainerViewParent> 和 Container<视图:UIViewController>。

这些类是为了创建视图或视图控制器而设计的。以下是一些示例

第一种情况是针对 UIView

import UIKit
import UIContainer

class ExampleViewController: UIViewController {
    weak var containerView: ContainerView<CartView>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let containerView = ContainerView<CartView>(in: self) // Will be explained
        self.view.addSubview(containerView)
        
        self.containerView = containerView
    }
}

另一种情况是使用 Container 针对 UIViewController

import UIKit
import UIContainer

class ExampleViewController: UIViewController {
    weak var containerView: Container<CartViewController>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let containerView = Container<CartViewController>(in: self) // Will be explained
        self.view.addSubview(containerView)
        
        self.containerView = containerView
    }
}

现在,我唯一喜欢的方法是在我的视图内部创建子类,并在视图控制器中使用它

import UIKit
import UIContainer

class CartView: View {
    
    override func prepare() {
        super.prepare()
        
        // Do stuff here
    }
}

extension CartView {
    class Container: ContainerView<CartView> {        
        override func containerDidLoad() {
            super.containerDidLoad()
            
            // Stylize your container
        }
    }
}

class ExampleViewController: UIViewController {
    weak var cartView: CartView.Container!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let cartView = CartView.Container(in: self) // Will be explained
        self.view.addSubview(cartView)
        
        self.cartView = cartView
    }
}

请记住,Container 和 ContainerView 具有相同的方法和生命周期。containerDidLoad 会在生命周期结束时调用,并且在将视图装载到其内部后,它尚未在父视图中呈现。

2. UIContainerCell

此协议是 UIContainer 的封装,帮助创建表格单元格。它尚未完全实现,因为我们还有诸如 UICollectionViewCell 这样的其他情况尚未测试,但您可以使用 ContainerCollectionViewCell 尝试它。

这里的魔法在于 UIContainerCell 已经实现了实现此协议的类的必需方法。当您处于 tableView.dequeueReusableCell 状态时,应调用 cell.prepareContainer(inside: self),请记住,您的单元格必须是 UIContainerCell 衍生。

prepareContainer 防止重新创建视图,并保持作为可重用单元格的安全。如果您在使用具有 DisposeBag 的视图的响应式库时,这是一个重要因素。

有两个类,一个是针对 UIView,另一个是针对 UIViewController。第一个是 ContainerTableViewCell,第二个是 ContainerTableCell。它们具有相同的方法和调用栈。

因此,曾经用作 UITableViewCell 的视图现在只是 UIView,您该如何做到这一点?以下是一个示例

import UIKit
import UIContainer

class CartView: View, ContainerCellDelegate {
    override func prepare() {
        super.prepare()
        
        self.applyLayouts()
        self.configureButtons()
    }
    
    func reloadView(_ cart: Cart) {
        self.titleLabel.text = cart.name;
        
        self.onPlayTouch = {
            cart.startRunning()
        }
        
        self.onStopTouch = {
            cart.stopRunning()
        }
    }
}

class CartsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.tableView.register(ContainerTableViewCell<CartView>.self, forCellReuseIdentifier: "CartViewCellIdentifier")
        
        self.tableView.delegate = self
        self.tableView.dataSource = self
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CartViewCellIdentifier", for: indexPath) as! ContainerTableViewCell<CartView>
        
        cell.prepareContainer(in: self)
        cell.view.reloadView(carts[indexPath.row])
        
        return cell
    }
}

2.a) ContainerCellDelegate

注意 CartView 超类类型中的 ContainerCellDelegate 符合性。ContainerCellDelegate 是我们的助手,因为它可以或不能用作单元格,具体取决于您是否将其创建为 ContainerView 示例。

ContainerCellDelegate 有两种行为,它将 cellDelegate: Delegate 关联类型添加到视图。第一种行为是如果您没有在视图类中指定添加 var cellDelegate: Delegate? 行。会发生什么?ContainerCellDelegate 会理解您没有指定单元格委托,并将 EmptyCellDelegate 与您的类关联。请注意,如果您键入 "self.cellDelegate",类型将是 EmptyCellDelegate?。

3. UIContainerStoryboard

这是一个为以下场景创建的协议:您希望使用Storyboard或.xib将视图添加到其他视图内部。您需要实现其Storyboard视图并提供一些初始化期间的帮助方法。以下是一些示例

import UIKit
import UIContainer

class CartViewStoryboard: View, UIContainerStoryboard {
    typealias View = CartView
    weak var containerView: ContainerView<CartView>!
    
    var edgeInsets: UIEdgeInsets = .init(top: 15, left: 15, bottom: 15, right: 15)
    
    func containerDidLoad() {
        // stylize the container
    }
}

class ExampleViewController: UIViewController {
    @IBOutlet weak var cartWrapper: CartViewStoryboard!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.cartWrapper.prepareContainer(inside: self)
    }
}

4. ContainerController

ContainerController通过创建符合ViewController的视图来帮助。您只需要将您的视图符合ViewControllerType协议,并使用ViewControllerMaker.dynamic(_: (UIViewController) -> Void)实现content: ViewControllerMaker { get}。

之后,您应该使用ContainerController.init(_: View)创建UIViewController实例,其中View符合ViewControllerType。

5. WindowContainer

WindowContainer是我们最新的新特性,可能会在某些版本更新中被更改。它只能与没有类或特殊方法的淡入效果一起使用。

要使用此功能,您必须在项目中实现WindowContainerType,并实现启动器静态函数和容器获取器。要设置AppDelegate类中的窗口,应调用UIWindow.container(WindowContainerType.self)。以下是一个示例

import UIKit
import UIContainer

enum WindowType {
    case main
}

extension WindowType: WindowContainerType {
    var container: UIView! {
        switch self {
        case .main:
            return Container(in: AppDelegate.shared.windowView) {
                MainViewController.shared // Provided by ViewSharedContext
            }
    }
    
    static func launcher(in windowContainer: WindowContainer<WindowType>) -> UIView! {
        return Container(in: windowContainer) {
            UIViewController.laucherViewController()
        }
    }
}

现在,AppDelegate

import UIContainer

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var windowView: WindowContainer<WindowType> {
        return window!.rootViewController as! WindowContainer<WindowType>
    }
    
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
     
        self.window = UIWindow.container(WindowType.self)
        self.window?.makeKeyAndVisible()
        self.windowView.transition(to: .main)
        
        return true
     }
}

作者

brennobemoura, [email protected]

许可

UIContainer遵从MIT许可。更多信息请参阅LICENSE文件。