Skip to content

Latest commit

 

History

History
300 lines (193 loc) · 8.59 KB

File metadata and controls

300 lines (193 loc) · 8.59 KB

Model View Controller

Agenda

  • MVC
  • Organizing our project
  • Working without a storyboard
  • Navigation programmatically
  • Extending our project

Learning Objectives

By the end of this lesson, students should be able to:

  • Describe and use MVC in an Xcode project
  • Implement navigation programmatically
  • Send information between view controllers programmatically

Architectural Patterns

Eventually projects get bigger: more swift files, xib files, assets, etc.

If we are not careful we'll end up with spaghetti code 🍝

  • without structure
  • difficult to follow
  • hard to maintain

We can avoid this by using an architectural pattern.

"An architectural pattern is a general, reusable solution to a commonly occurring problem in software architecture within a given context"

MVC

MVC is Apple's recommended architecture for iOS apps.

It's made up of three main objects:

  • The Model: Where your data lives.
  • The View: What the user sees.
  • The Controller: Mediator between the view and the model.

The model

  • Model Objects (classes, structs, etc)

The view

Often reusable and doesn't handle any business logic.

  • UIView subclasses

How to know if we are doing views right?

Does it interact with the model layer?
Does it contain any business logic?
Does it try to do anything not related to UI?

All of these questions normally should be a no, otherwise the view is already doing more than needed. This is a standard check but not necessarily must be true 100% of times.

The controller

The least reusable part of an app 😰

What are its responsibilities? It's basically the 🧠 of the app.

  • Order of method calls.
  • Refreshing the app.
  • Presenting new views.
  • Sending object between views.
  • Handle user interaction (What happens after the user taps a button?)

In Class Activity (10 min)

From what you just learned, draw your own version of the MVC diagram and review the responsibilities for each component.

Can you explain it using a real world scenario with an analogy?

Jamboard Link

File Structure

You can take your Subscription Box project and arrange your files with MVC in mind.

  1. Right click on the project navigator → New Group
  2. Move files accordingly

files

Navigation

So far we've been building apps with one screen. Most of the time you will need to interact with two screens or more, and we should handle these transitions.

  • Navigation can be done using Segues or programmatically. We'll cover the second option.

Follow along with the Starter Code

Navigation Programmatically

Steps to setup a project with without a Storyboard.

  1. Delete the storyboard file.
  2. Remove the storyboard name on Project Navigator > Select Project > General > Deployment Info > Main Interface
  3. Remove the Storyboard name from the .plist
  4. Change the SceneDelegate to tell our app what to use as the initial ViewController.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(frame: UIScreen.main.bounds)
        let viewController = //An instance of your view controller goes here
        window?.rootViewController = viewController
        window?.makeKeyAndVisible()
        window?.windowScene = windowScene
    }
Programmatically we defined an instance of the view controller and set it as the root view controller of the main window.

Changing the rootViewController from another View Controller:

self.view.window!.rootViewController = //Instance of the VC you want to set as root

Creating a Navigation Controller

When using the storyboard, we would embed view controllers in navigation controllers in the interface builder.

We can also do it programmatically:

let navigationController = UINavigationController(rootViewController: //some VC)
self.view.window!.rootViewController = navigationController

//or

present(navigationController, animated: true, completion: nil) present it on top of an existing VC

Having a navigation controller gives us a navigation bar and the stack to show and dismiss view controllers.

Presenting a VC with the sliding left animation

let nextVC = ViewController()
self.navigationController?.pushViewController(nextVC, animated: true)

Presenting a VC with the sliding up animation

let nextVC = ViewController()
self.navigationController?.present(nextVC, animated: true, completion: nil)

Dismissing a View Controller to go back to a previous screen

If you used the present method:

self.dismiss(animated: true, completion: nil)

If you used the push method:

self.navigationController?.popViewController(animated: true)

Return to root:

self.navigationController?.popToRootViewController(animated: true)

Passing Information

We assign the value of properties right after creating the instance of the second view controller.

let nextVC = ViewController()
nextVC.color = UIColor.red //The instance of ViewController has a property called `color` and we are sending over the value `UIColor.red` to use it later in the next VC.

Lab Time

Activity Link

Changing rootViewController with an animation (cool thing to know)

Scenarios:
The end of an onboarding flow.
User logged out.

extension UINavigationController {
    /**
     It removes all view controllers from navigation controller then set the new root view controller and it pops.

     - parameter vc: root view controller to set a new
     */
    func initRootViewController(vc: UIViewController, transitionType type: String = "kCATransitionFade", duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.viewControllers.removeAll()
        self.pushViewController(vc, animated: false)
        self.popToRootViewController(animated: false)
    }

    /**
     It adds the animation of navigation flow.

     - parameter type: kCATransitionType, it means style of animation
     - parameter duration: CFTimeInterval, duration of animation
     */
    private func addTransition(transitionType type: String = "kCATransitionFade", duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        transition.type = CATransitionType(rawValue: type)
        self.view.layer.add(transition, forKey: nil)
    }
}

Additional Resources

Apple Documentation on MVC
About App Development with UIKit
MVC analogy
More on MVC
Wikipedia - architectural pattern MVC tutorial with networking Manual Navigation - article