Search code examples
iosswiftuikit

Should we be using UIView or UIViewController for the following feature?


We have the following app, where user can switch to different "page" (purple, yellow, ... colours) from side menu.

enter image description here

I was wondering, should the "page" be implemented as UIView, or should the "page" be implemented as UIViewController?

The pages shall responsible to

  • Read/ write from/ to CoreData.
  • Possible holding a UIPageView, which user can swipe through multiple child pages as shown in https://i.sstatic.net/v0oNo.gif
  • Holding a UICollectionView.
  • User can drag and move the items in the UICollectionView
  • User can perform various contextual action (Delete, clone, ...) on the items in UICollectionView.
  • Can easily port to iPad in the future.

Currently, my implementation of using UIView are as follow.

private func archive() {
    if let trashView = self.trashView {
        trashView.removeFromSuperview()
        self.trashView = nil
    }
    
    if self.archiveView != nil {
        return
    }
    
    let archiveView = ArchiveView.instanceFromNib()
    
    self.view.addSubview(archiveView)
    archiveView.translatesAutoresizingMaskIntoConstraints = false
    archiveView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
    archiveView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
    archiveView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    archiveView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    
    self.archiveView = archiveView
}

private func trash() {
    if let archiveView = self.archiveView {
        archiveView.removeFromSuperview()
        self.archiveView = nil
    }
    
    if self.trashView != nil {
        return
    }
    
    let trashView = TrashView.instanceFromNib()
    
    self.view.addSubview(trashView)
    trashView.translatesAutoresizingMaskIntoConstraints = false
    trashView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
    trashView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
    trashView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    trashView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    
    self.trashView = trashView
}

I notice that if I implement the "pages" using UIView, I will lost some capability of UIViewController like

  • viewDidLoad callback.
  • viewWillLoad callback.
  • viewDidLayoutSubviews callback.
  • ...

However, I am not clear whether losing those capabilities will stop me from implementing a proper "page"?

May I know, should I implement those "pages" using UIView, or using UIViewController?


Solution

  • I would do this with UIViewController because of all the UIKit callback reasons you listed in the question already.

    I assume that you have a UINavigationController instance that's set as window.rootViewController for your app. You have a reference to this instance using which you can easily switch between different screens.

    Example

    class SlideMenuViewController: UIViewController {
        enum Option {
            case archive
            case trash
        }
        
        var onSelect: ((_ option: Option) -> Void)?
    }
    
    class ArchiveViewController: UIViewController {}
    class TrashViewController: UIViewController {}
    
    class AppNavigator {
        
        let mainNavigationController: UINavigationController
        init(navigationController: UINavigationController) {
            self.mainNavigationController = navigationController
        }
        
        private lazy var slideMenuVC: SlideMenuViewController = {
            let slideMenu = SlideMenuViewController()
            slideMenu.onSelect = { [weak self] (option) in
                self?.openScreen(for: option)
            }
            return slideMenu
        }()
        
        private lazy var archiveVC: ArchiveViewController = {
            return ArchiveViewController()
        }()
        
        private lazy var trashVC: TrashViewController = {
            return TrashViewController()
        }()
        
        func openScreen(for option: SlideMenuViewController.Option) {
            let targetVC: UIViewController
            switch option {
            case .archive: targetVC = archiveVC
            case .trash: targetVC = trashVC
            }
            mainNavigationController.setViewControllers([targetVC], animated: true)
        }
        
    }