• Version control can be cumbersome • Debug can be hard • Use several storyboards! Split your app. • Good approach for small apps • Number of developers... 1?
cross platform! The UI code should be reusable on all devices. • No backward compatibility (only iOS 13 +) • Hot Reload can be frustrating • Much less documentation available • Good approach to new apps • Way easier than storyboards
app process • Configure your initial scenes • Process events and lifecycle • Scene Delegate manages one instance of your app's user interface • So if the user has created two windows showing your app, you have two scenes, both backed by the same app delegate • Your app no longer moves to background, scenes do! • UI events and lifecycle
application level lifecycle events • AppDelegate role has changed from iOS 12 -> 13 • In the default template you will find three (3) methods • Whan app is opened • application(_:didFinishLaunchingWithOptions:) • When creating new scenes (it's basically a window) • Discards a scene • Notice that in addition to these, there are other lifecycle events (when app terminates for example)
Allows to build multi-window apps on iOS and iPadOS • App Delegate has functions related to the management of scene sessions • application(_:configurationForConnecting:options:) • Needs a return configuration object when creating a new scene • Can be used to restore the state of a scene • application(_:didDiscardSceneSessions:) • Called when app's user closed one or more scene via the app switcher (according to the doc)
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { NSLog("AppDelegate: First launch") return true } func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { NSLog("AppDelegate: New Scene created") return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { NSLog("AppDelegate: Scene discarded") } } when new scenes are created. Returns configuration object that hold information which storyboard to use (info.plist). You can decide which configuration to use. When the user removes a scene from the app switcher. If your app is not running, UIKit calls this method the next time your app launches.
views. Window's view is the visible part in the screen. • To add something to the screen, it's done using the UIWindow object. • View - UIView • Define a portion of a window that fills some content. Can be image, text or shape. • UIKit provides predefined views that you can use: UIButton, UILabel etc. • View can be a container for other views • View Controller - UIViewController • Class that acts controller between the model and view
app's user interface • Windows work with views (usually through view controllers) • After window is created, it stays the same and only the views displayed by it change • Every iOS app has at least one Window • If need for an external display (for example Apple TV) then another Window object is used. • To display a view in window, add the view as an subview of window!
an instance of UIView (or one of it's subclasses) • Responsible for • drawing content • handling touch events • managing layout for subviews • Parent view (superview) is responsible for positioning and sizing their child views (subviews) • Changing the superview you may influence the subviews, position for example
app's data and visual appearance • Displayed content should go always through a view controller (content view controller) • When implementing iOS app you have multiple view controller's for each screen. • View Controller can be also a container view controller • A view controller holding other view controllers! • Several built-in content view controllers available, like navigation or tab view controller
Model – View – Controller • Model -> Holds data of your app • View -> either .xib or .storyboard • Controller -> controls the model and the view • When creating a new "screen" to our app, you typically have 1.Controller class 2.View either in .xib or in .storyboard 28.10.2020 ESITYKSEN NIMI / TEKIJÄ 32
5 -> where you can design and build a UI for your apps. • One file may contain several screens! • Good • Better overview of all the screens in your app • Describes transitions between screens, simply ctrl-drag from screen to another to create "segue" • Storyboards are nice for apps with small or medium number of screens • Bad • Storyboard will get very confusing if you have lot of screens. • You need a big screen • Apple seems to be pushing the storyboard approach • Apple seems to be pushing towards SwiftUI
controls it's view. • The view is often a "root view" containing other views • View Controller is usually your own class, extending UIViewController • When setting some view controller as a rootViewController of the UIWindow, then window will automatically add the view controller's view to subview (and visible to screen.) • When you have storyboard declared in info.plist 1. main.storyboard file is opened 2. it's initial view controller is set as root view controller 3. root view controllers view is added to window
you subclass UIViewController • The View Controller controls all the views in that "screen" • View Controller usually responds to user events happening to those views • Button presses, touches and so on • Usually the views are designed using Interface builder.
• allocate and load data to be displayed in view • viewDidAppear • when view is visible • viewDidDisappear • when view is not visible • didReceiveMemoryWarning • Respond to low memory notifications
and view controllers • When creating your template app, you have storyboard holding your view controller and view. • From identity inspector you can see that the view controller is linked to a file called ViewController.swift • From attributes inspector you can simulate different screen sizes
variable that is annotated with the symbol IBOutlet • The value of the outlet is specified graphically in a storyboard • Example: declare a UILabel as an outlet • @IBOutlet weak var myButton: UIButton! • Drag UIButton to Storyboard and ctrl-drag to ViewController - code Demo
object holds information necessary to send to another object when an event occurs. • "Which method is called when button is clicked?" • An action method must have a certain form: • @IBAction func buttonClicked(sender: AnyObject) {..} • You can set target and action in code or using Interface Builder Demo
you lay out app's UI elements creating relationships between elements • These relationships are called constraints • You can create constraints to an element or a group of elements • Basic idea is to create UI that works on different screen sizes and orientation 28.10.2020 ESITYKSEN NIMI / TEKIJÄ 40
• Rules about how to layout elements • Examples • specify element width • specify horizontal distance from another element • Auto Layout calculates all constraints at the same time and tries to layout your UI 28.10.2020 ESITYKSEN NIMI / TEKIJÄ 41
ways • Control-drag • Drag from view to view and select the constraint you want to use • Align and Pin Menus • On the bottom of the canvas, you can see a small icon group. Select either align or pin 28.10.2020 ESITYKSEN NIMI / TEKIJÄ 42
through UIWindow • UIWindow has a property rootViewController that provides the content view of the visible window. • You can change this at runtime in view controller • self.view.window?.rootViewController = SecondScreenController(nibName: "SecondScreen", bundle: nil)
• Problem is that we have to define navigation programmatically. How user is coming back? What kind of visual cues are we giving to the user about the navigation? • Instead it's good practice to use container view controllers • Container view controller contains other view controllers • UINavigationController • UITabController • There are couple ready made content view controllers, but you can also build our own.
screens using a navigation stack • Bottom stack: root view controller • Top of the stack: view controller that is displayed currently • To add a new screen: • pushViewController:animated: • To remove the displayed screen • popViewControllerAnimated: • Navigation controller provides you a back button, that pops the current view
no code! • How? • Select your root view controller • Choose Menu: Editor » Embed in » Navigation Controller • Now your view controller is embedded inside of a navigation controller! • Create new view controller to storyboard • Create button to first view controller and ctrl-drag to the new view controller. Choose push and you now have a new navigation from view to another!
the navigation stack, moving the source view controller out of the way providing a back button to navigate back to the source – on all devices • Show Detail • Replaces the detail/secondary view controller when in a UISplitViewController with no ability to navigate back to the previous view controller. Example: Mail in iPad in landscape when tapping mail title. • Present Modally • Presents a view controller in various different ways as defined by the Presentation option, covering up the previous view controller - most commonly used to present a view controller that animates up from the bottom and covers the entire screen on iPhone • Present as popover • When run on iPad, the destination appears in a small popover, and tapping anywhere outside of this popover will dismiss it.
segue should perform, add shouldPerformSegueWithIdentifier - method to your view controller • This method is called before the segue should start. • In the method, return true if you want to open the next View Controller override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { return false }
controller that you use to divide your app into two or more distinct modes of operation • Tab bar holds multiple tabs, each represented by a child view controller • Selecting the tab causes the view controller's view to be displayed on the screen
consists of • UITabBarController object • One content view controller for each tab • Really easy to use • Allocate the UITabController • Use viewControllers property to set the custom view controllers • Add the UITabController to be rootViewController • Or use Storyboard
title and image, you do this in your custom view controller • The UIViewController holds a property tabBarItem (UITabBarItem) that you can use to change the current tab bar • The default title of the tab is the title of the view controller. • So changing the title of view controller will influence the tab to have a title also!
scroll the table view, insert or delete rows and selections • UITableView inherites UIScrollView, defines scrolling as default • Table View Controller • UITableViewController • Adds support for many standard table related behaviors. Minimize the amount of code you have to write. • Subclass UITableViewController • Data Source and Delegate • UITableView must have delegate and data source: UITableViewDataSource and UITableViewDelegate
Table View • Managing Selections • Modifying Header and Footer • Editing Table Rows • Clicking a Row • See UITableViewDelegate documentation for all methods, all optional functions
= ["Luke Skywalker", "R2-D2"] @IBOutlet weak var tableview: UITableView! override func viewDidLoad() { super.viewDidLoad() self.tableview.delegate = self self.tableview.dataSource = self } ... } This data will be displayed tableview is in xib let's pass delegate and datasource as self. This class must conform to the protocols
"R2-D2"] override func viewDidLoad() { super.viewDidLoad() let xibfile = UINib(nibName: "mycell", bundle: nil) self.tableView.register(xibfile, forCellReuseIdentifier: "mikkihiiri") } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dummyData.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let secondCell = tableView.dequeueReusableCell(withIdentifier: "mikkihiiri")! secondCell.textLabel?.text = dummyData[indexPath.row] return secondCell } } Register the separate xib file And then we can just use it without if ...
receiving stuff, you will get NSData object • It is asynchronous • The URLSession.shared has method func dataTask(with: URL, completionHandler: (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
error) in if let httpResponse = response as? HTTPURLResponse { print("statusCode: \(httpResponse.statusCode)") } } HTTPURLResponse is a subclass of URLResponse
"https://pohjus-rest-location.herokuapp.com/locations") } func fetch(url: String) { let myURL = URL(string: url)! let httpTask = URLSession.shared.dataTask(with: myURL) { (optionalData, response, error) in self.parse(data: optionalData) } httpTask.resume() } func parse(data: Data?) { if let d = data { print(String(data: d, encoding: String.Encoding.utf8)!) } } When view appears start fetching When view appears start fetching and call parse Outputs data as String
try jsonDecoder.decode(Array<Location>.self, from: optionalData!) print(stuff) } catch let error { print(error) } Custom struct, must conform to Codeable
func parse(data: Data?) { if let d = data { let jsonDecoder = JSONDecoder() do { let stuff = try jsonDecoder.decode(Array<Location>.self, from: d) print(stuff) } catch let error { print(error) } } } func fetch(url: String) { let myURL = URL(string: url)! let httpTask = URLSession.shared.dataTask(with: myURL) { (optionalData, response, error) in self.parse(data: optionalData) } httpTask.resume() } Now we have an array full of location objects