0

I am trying to create a full page sized horizontally scrolling UIScrollView. On each page I am adding instances of the same UIViewController class. I would like to create some kind of reusable functionality to conserve both memory and processor use needed. Below is a basic implementation I have created with some toying around with how reusability might work although Im not quite sure. Thank you for any help you can offer.

Current UIScroll ViewController Model

   let scrollView:UIScrollView = {
   let scrollView = UIScrollView(frame: CGRect.zero)
    scrollView.isPagingEnabled = true
    scrollView.backgroundColor = UIColor.gray
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.showsVerticalScrollIndicator = false
    scrollView.showsHorizontalScrollIndicator = true
    return scrollView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    scrollView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: self.view.bounds.size)
    self.view.addSubview(scrollView)
    scrollView.delegate = self
    scrollView.contentSize = CGSize(width: 3 * self.view.frame.width, height: self.view.frame.height)

    NSLayoutConstraint.activate([
        scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
        scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
        scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0),
        scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
        ])

    let viewController1 = UIViewController()
    viewController1.view.backgroundColor = UIColor.red
    let viewController2 = UIViewController()
    viewController2.view.backgroundColor = UIColor.blue
    let viewController3 = UIViewController()
    viewController3.view.backgroundColor = UIColor.green

    self.addChild(viewController1)
    viewController1.view.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: self.view.bounds.size)
    scrollView.addSubview(viewController1.view)
  addViewControllerContraints(viewController: viewController1, index: 0)
    viewController1.didMove(toParent: self)

    self.addChild(viewController2)
    viewController2.view.frame = CGRect(origin: CGPoint(x: self.view.bounds.width, y: 0), size: self.view.bounds.size)
    scrollView.addSubview(viewController2.view)
    addViewControllerContraints(viewController: viewController2, index: 1)
    viewController2.didMove(toParent: self)

    self.addChild(viewController3)
    viewController3.view.frame = CGRect(origin: CGPoint(x: 2 * self.view.bounds.width, y: 0), size: self.view.bounds.size)
    scrollView.addSubview(viewController3.view)
    addViewControllerContraints(viewController: viewController3, index: 2)
    viewController3.didMove(toParent: self)

}

func addViewControllerContraints( viewController: UIViewController, index:Int){
    guard let view = viewController.view else{
        print("View found nil")
        return
    }
    view.translatesAutoresizingMaskIntoConstraints = false
    let offset:CGFloat = UIScreen.main.bounds.width * CGFloat(index)
    print("Offset: \(offset)")
    NSLayoutConstraint.activate([
        view.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0),
        view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: offset),
        view.heightAnchor.constraint(equalToConstant: self.view.bounds.height),
        view.widthAnchor.constraint(equalToConstant: self.view.bounds.width)
        ])
} 

Is there a good way to create some type of reuse functionality this is something I was playing around with based on This Answer although I realize that is primarily for UIPageViewControllers where allocation and deallocation of UIViewController's is handled for you.

Possible Reuse Functionality

var reuseableViewControllers:[UIViewController] = [UIViewController]()

private func unusedViewController() -> UIViewController {
    let unusedViewControllers = reuseableViewControllers.filter { $0.parent == nil }
    if let someUnusedViewController = unusedViewControllers.first {
        return someUnusedViewController
    } else {
        let newViewController = UIViewController()
        reuseableViewControllers.append(newViewController)
        return newViewController
    }
}
  • How many pages do you have? It is probably just easier to store the view controller for each page in an array and when that page is requested you either allocate a new instance (the first time the page is requested) or return the existing one (subsequent requests). – Paulw11 Apr 08 '19 at 22:28
  • I am creating a model where in theory the user could have infinitely many pages. I would like to do some kind of a caching of UIViewControllers similar to an array as you mention but I am not sure how to handle allocation and deallocation in an efficient manner. I wonder if it is possible to have three view controllers live at any time so the user can swipe forwards and backwards without have to have all of the UIViewControllers initialized. – TheRedCamaro3.0 3.0 Apr 08 '19 at 22:33
  • Scrollview paging often does that; you have an onscreen and offscreen left/right views. As the use scrolls you swap them around and reset the scrollview content offset. What you really need is an efficient way of setting the state of the view controllers as you scroll – Paulw11 Apr 08 '19 at 22:46
  • @Paulw11 How would you recommend efficiently setting the state as the user scrolls so as not to use too much memory or processor? – TheRedCamaro3.0 3.0 Apr 09 '19 at 00:26
  • It is hard to answer; I don't know what you are showing in your views. In general, be wary of pre-optimising - If you have a problem then look at it, but don't create complex solutions to a problem that doesn't exist. Make sure you are using image caching (SDWebImage or Kingfisher for example) if there are resources that are fetched from the web. While iOS devices are memory constrained compared to a PC, they still have reasonable amounts of RAM and holding something in memory (or persisting it to disk) is always cheaper than fetching it from the network. – Paulw11 Apr 09 '19 at 00:31
  • @Paulw11 I plan on showing a collectionView with photos in a grid format. I plan to use SD_WebImage to cache the images. I am concerned about the foundation of having to reinitialize a view controller every time the user swipes and simultaneously deallocate a ViewController. Is there an efficient way to do this as well as deallocate the viewcontrollers that are no longer in use. – TheRedCamaro3.0 3.0 Apr 09 '19 at 01:08
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191487/discussion-between-paulw11-and-theredcamaro3-0-3-0). – Paulw11 Apr 09 '19 at 01:11

0 Answers0