0% found this document useful (0 votes)
143 views208 pages

224 Modernizing Your Ui For Ios 13 PDF

Uploaded by

Anuj Vats
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
143 views208 pages

224 Modernizing Your Ui For Ios 13 PDF

Uploaded by

Anuj Vats
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 208

#WWDC19

Modernizing Your UI for iOS 13

David Duncan, iOS System Experience

© 2019 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.

Flexible UI

Bars

Presentations

Search

Gestures

Menus

Flexible UI

David Duncan
Launch Storyboards
Launch Storyboards
Launch Images
Be Resizable
Be Resizable
Be Resizable
Actions

Adopt Launch Storyboards

Support any size

Support Split Screen Multitasking

Required April 2020


Bars
New Bar Appearance

Free when you link against iOS 13

Appearance Customization
let appearance = UINavigationBarAppearance()

appearance.configureWithOpaqueBackground()

appearance.titleTextAttributes = [.foregroundColor: myAppLabelColor]


appearance.largeTitleTextAttributes = [.foregroundColor: myAppLabelColor]

navigationBar.standardAppearance = appearance
let appearance = UINavigationBarAppearance()

appearance.configureWithOpaqueBackground()

appearance.titleTextAttributes = [.foregroundColor: myAppLabelColor]


appearance.largeTitleTextAttributes = [.foregroundColor: myAppLabelColor]

navigationBar.standardAppearance = appearance
let appearance = UINavigationBarAppearance()

appearance.configureWithOpaqueBackground()

appearance.titleTextAttributes = [.foregroundColor: myAppLabelColor]


appearance.largeTitleTextAttributes = [.foregroundColor: myAppLabelColor]

navigationBar.standardAppearance = appearance
let appearance = UINavigationBarAppearance()

appearance.configureWithOpaqueBackground()

appearance.titleTextAttributes = [.foregroundColor: myAppLabelColor]


appearance.largeTitleTextAttributes = [.foregroundColor: myAppLabelColor]

navigationBar.standardAppearance = appearance
let appearance = UINavigationBarAppearance()

appearance.configureWithOpaqueBackground()

appearance.titleTextAttributes = [.foregroundColor: myAppLabelColor]


appearance.largeTitleTextAttributes = [.foregroundColor: myAppLabelColor]

navigationBar.standardAppearance = appearance
// Customizing

.standardAppearance
// Customizing

.compactAppearance
// Customizing

.scrollEdgeAppearance
// Customizing

.buttonAppearance .doneButtonAppearance
Customizing Bars

UIToolbar: UIToolbarAppearance

UITabBar: UITabBarAppearance
// Customizing

.stackedLayoutAppearance

.inlineLayoutAppearance

.compactInlineLayoutAppearance
let appearance = navigationBar.standardAppearance.copy()

// Configure whatever else you want

navigationItem.standardAppearance = appearance
let appearance = navigationBar.standardAppearance.copy()

// Configure whatever else you want

navigationItem.standardAppearance = appearance
let appearance = navigationBar.standardAppearance.copy()

// Configure whatever else you want

navigationItem.standardAppearance = appearance
let appearance = navigationBar.standardAppearance.copy()

// Configure whatever else you want

navigationItem.standardAppearance = appearance

Presentations

Russell Ladd
Sheets
Sheet
UIModalPresentationStyle.pageSheet
UIModalPresentationStyle.pageSheet
.formSheet
Readable Width
Readable Width
Readable Width
UIModalPresentationStyle.pageSheet
.formSheet
NEW

UIModalPresentationStyle.automatic
.pageSheet
.formSheet
class MyViewController: UIViewController {

func pickPhoto() {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: true)
}

func presentCamera() {
let camera = UIImagePickerController()
camera.sourceType = .camera
present(camera, animated: true)
}
}
class MyViewController: UIViewController {

func pickPhoto() {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: true)
}

func presentCamera() {
let camera = UIImagePickerController()
camera.sourceType = .camera
present(camera, animated: true)
}
}
class MyViewController: UIViewController {

func pickPhoto() {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: true)
}

func presentCamera() {
let camera = UIImagePickerController()
camera.sourceType = .camera
present(camera, animated: true)
}
}
class MyViewController: UIViewController {a

func showOptions() {a
let optionsVC = MyOptionsViewController()
present(optionsVC, animated: true)
}a

func showCustomCamera() {a
let cameraVC = MyCameraViewController()
present(cameraVC, animated: true)
}a
}a
class MyViewController: UIViewController {a

func showOptions() {a
let optionsVC = MyOptionsViewController()
present(optionsVC, animated: true)
}a

func showCustomCamera() {a
let cameraVC = MyCameraViewController()
present(cameraVC, animated: true)
}a
}a
class MyViewController: UIViewController {a

func showOptions() {a
let optionsVC = MyOptionsViewController()
present(optionsVC, animated: true)
}a

func showCustomCamera() {a
let cameraVC = MyCameraViewController()
present(cameraVC, animated: true)
}a
}a
class MyViewController: UIViewController {a

func showOptions() {a
let optionsVC = MyOptionsViewController()
present(optionsVC, animated: true)
}a

func showCustomCamera() {a
let cameraVC = MyCameraViewController()
cameraVC.modalPresentationStyle = .fullScreen
present(cameraVC, animated: true)
}a
}a
class MyViewController: UIViewController {

func showOptions() {
let optionsVC = MyOptionsViewController()
optionsVC.modalPresentationStyle = .popover
present(optionsVC, animated: true)
}
}
class MyViewController: UIViewController {

func showOptions() {
let optionsVC = MyOptionsViewController()
optionsVC.modalPresentationStyle = .popover
present(optionsVC, animated: true)
}
}
NEW
class EmailController: UIViewController, UIAdaptivePresentationControllerDelegate {

func draftDidChange() {
isModalInPresentation = draft.hasChanges
}

func presentationControllerDidAttemptToDismiss(_: UIPresentationController) {


// Present action sheet
}
}
NEW
class EmailController: UIViewController, UIAdaptivePresentationControllerDelegate {

func draftDidChange() {
isModalInPresentation = draft.hasChanges
}

func presentationControllerDidAttemptToDismiss(_: UIPresentationController) {


// Present action sheet
}
}
NEW
class EmailController: UIViewController, UIAdaptivePresentationControllerDelegate {

func draftDidChange() {
isModalInPresentation = draft.hasChanges
}

func presentationControllerDidAttemptToDismiss(_: UIPresentationController) {


// Present action sheet
}
}
User Begins Pulling User Releases with Intent

presentedViewController
.isModalInPresentation
True

presentationController-
DidAttemptToDismiss
User Begins Pulling User Releases with Intent

presentedViewController
.isModalInPresentation
True

presentationController-
False
DidAttemptToDismiss

False
presentationController-
ShouldDismiss

True

presentationController- presentationController-
WillDismiss DidDismiss
User Begins Pulling User Releases with Intent

presentedViewController
.isModalInPresentation
True

presentationController-
False
DidAttemptToDismiss

False
presentationController-
ShouldDismiss

True

presentationController- presentationController-
WillDismiss DidDismiss
Share Extensions
,,,

Principal View Controller


• Set isModalInPresentation

• Respond to
presentationControllerDidAttemptToDismiss
Share Extensions
,,,

Principal View Controller


• Set isModalInPresentation

• Respond to
presentationControllerDidAttemptToDismiss
Share Extensions
,,,

Principal View Controller


• Set isModalInPresentation

• Respond to
presentationControllerDidAttemptToDismiss
Share Extensions
,,,

Principal View Controller


• Set isModalInPresentation

• Respond to
presentationControllerDidAttemptToDismiss
Appearance Callbacks
Full Screen

Presentation Dismissal

presentingViewController viewWillDisappear viewDidDisappear viewWillAppear viewDidAppear

presentedViewController viewWillAppear viewDidAppear viewWillDisappear viewDidDisappear


Appearance Callbacks
Page and Form Sheet

Presentation Dismissal

presentingViewController viewWillDisappear viewDidDisappear viewWillAppear viewDidAppear

presentedViewController viewWillAppear viewDidAppear viewWillDisappear viewDidDisappear


UIWindow

rootViewController.view
UIWindow

Private UIKit View(s)

rootViewController.view
Updating Your App

Use sheets

Implement the modal flow


Search

Kyle Sluder
UISearchController
Cancel Button
UISearchBar
Scope Bar
Cancel Button
NEW
UISearchBar
Scope Bar
NEW
NEW
UISearchTextField
NEW
UISearchTextField
func loadView() {

let searchController = UISearchController(searchResultsController: /*...*/)

// Don’t automatically show the cancel button or scope bar


searchController.automaticallyShowsCancelButton = false
searchController.automaticallyShowsScopeBar = false

// Customize appearance of the search text field


let searchField = searchController.searchBar.textField
searchField.textColor = UIColor(named: "MyPinkColor")
searchField.font = UIFont(name: "American Typewriter", size: 18)

/* ... */
}
func loadView() {

let searchController = UISearchController(searchResultsController: /*...*/)

// Don’t automatically show the cancel button or scope bar


searchController.automaticallyShowsCancelButton = false
searchController.automaticallyShowsScopeBar = false

// Customize appearance of the search text field


let searchField = searchController.searchBar.textField
searchField.textColor = UIColor(named: "MyPinkColor")
searchField.font = UIFont(name: "American Typewriter", size: 18)

/* ... */
}
func loadView() {

let searchController = UISearchController(searchResultsController: /*...*/)

// Don’t automatically show the cancel button or scope bar


searchController.automaticallyShowsCancelButton = false
searchController.automaticallyShowsScopeBar = false

// Customize appearance of the search text field


let searchField = searchController.searchBar.textField
searchField.textColor = UIColor(named: "MyPinkColor")
searchField.font = UIFont(name: "American Typewriter", size: 18)

/* ... */
}
Search Results Controller
NEW

Search Results Controller


func loadView() {

let searchController = UISearchController(searchResultsController: /*...*/)

// Show the search results controller as soon as search is activated


searchController.showsSearchResultsController = true

self.navigationItem.searchController = searchController

/* ... */
}
func loadView() {

let searchController = UISearchController(searchResultsController: /*...*/)

// Show the search results controller as soon as search is activated


searchController.showsSearchResultsController = true

self.navigationItem.searchController = searchController

/* ... */
}
Search Tokens NEW

Available on any UISearchTextField

Copy, Paste

Drag and Drop


Search Tokens NEW

Available on any UISearchTextField

Copy, Paste

Drag and Drop


Tokens and Text

Tokens always precede text

Can be selected and deleted

Mixed selections allowed


Tokens and Text

Tokens always precede text

Can be selected and deleted

Mixed selections allowed


Tokens and Text

Tokens always precede text

Can be selected and deleted

Mixed selections allowed


Tokens and Text

Tokens always precede text

Can be selected and deleted

Mixed selections allowed


Creating Tokens
Creating Tokens

let selectedText = field.textIn(field.selectedTextRange) // "beach"


let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: field.selectedTextRange, with: token, at: field.tokens.count)
Creating Tokens

.selectedTextRange

let selectedText = field.textIn(field.selectedTextRange) // "beach"


let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: field.selectedTextRange, with: token, at: field.tokens.count)
Creating Tokens

let selectedText = field.textIn(field.selectedTextRange) // "beach"


let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: field.selectedTextRange, with: token, at: field.tokens.count)
Creating Tokens

let selectedText = field.textIn(field.selectedTextRange) // "beach"


let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: field.selectedTextRange, with: token, at: field.tokens.count)
Creating Tokens

let selectedText = field.textIn(field.selectedTextRange) // "beach"


let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: field.selectedTextRange, with: token, at: field.tokens.count)
Creating Tokens

let selectedText = field.textIn(field.selectedTextRange) // "beach"


let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: field.selectedTextRange, with: token, at: field.tokens.count)
Ranges and Positions

b e a c h
Ranges and Positions

UITextPosition

b e a c h
Ranges and Positions

UITextPosition

b e a c h
Ranges and Positions

.beginningOfDocument

b e a c h
.endOfDocument
Ranges and Positions

.beginningOfDocument

b e a c h

.endOfDocument
Ranges and Positions

.beginningOfDocument

b e a c h

.endOfDocument
Ranges and Positions

.textualRange

b e a c h
Ranges and Positions

textualRange.start

b e a c h

textualRange.end
Updating Your App

Take control with UISearchController

Customize appearance using UISearchTextField

Adopt UISearchToken

Check out the sample project


Gestures

James Magahern

Selection gestures in custom text views


Multiple selection gestures in tables and collections


Editing gestures
UITextInteraction NEW

Three lines of code

Editable and non-editable text interactions

Use UITextInput protocol to control selection UI


// Adding UITextInteractions to Your App

// Create selection interaction with type .editable or nonEditable


let selectionInteraction = UITextInteraction(for: .editable)

// Assign `textInput` property to your view that implements the UITextInput protocol
selectionInteraction.textInput = textView

// Add the interaction to the view


textView.addInteraction(selectionInteraction)

Selection gestures in custom text views


Multiple selection gestures in tables and collections


Editing gestures
shift
Multiple Selection

Your app might need to adapt


• "Select" turns into "Cancel" or "Done"

• Show action buttons

• Disable UI
Adopting Multiple Selection Gestures NEW

Only two delegate methods to implement

UITableView and UICollectionView


Adopting Multiple Selection Gestures NEW

Only two delegate methods to implement

UITableView and UICollectionView

optional func tableView(_ tableView: UITableView,


shouldBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath) -> Bool

optional func tableView(_ tableView: UITableView,


didBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath)
Adopting Multiple Selection Gestures NEW

Only two delegate methods to implement

UITableView and UICollectionView

optional func tableView(_ tableView: UITableView,


shouldBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath) -> Bool

optional func tableView(_ tableView: UITableView,


didBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath)
Adopting Multiple Selection Gestures NEW

Only two delegate methods to implement

UITableView and UICollectionView

optional func tableView(_ tableView: UITableView,


shouldBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath) -> Bool

optional func tableView(_ tableView: UITableView,


didBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath)

Selection gestures in custom text views


Multiple selection gestures in tables and collections


Editing gestures
Editing Gestures in iOS 13 NEW

Frees your app from adding additional UI

Unified across the entire system

Great for drawing apps

Completely free if you use UndoManager


// Working With or Without Productivity Gestures

public protocol UIResponder {


public var editingInteractionConfiguration: UIEditingInteractionConfiguration
}

public enum UIEditingInteractionConfiguration {


case `default` // System behavior, default
case none // Disable
}
Updating Your App

System selection gestures in your custom text view

Boost productivity with multiple selection gestures

Stop shaking your iPad


Menus

Mohammed Jisrawi

UIContextMenuInteraction

UIContextMenuInteraction

Rich previews

Complex hierarchies

Nested sub-menus

In-line sections

UIContextMenuInteraction

Rich previews

Complex hierarchies

Nested sub-menus

In-line sections

Consistent Gestures

3D Touch

Haptic Touch

Long press

Secondary click

Consistent Gestures

3D Touch

Haptic Touch

Long press

Secondary click

UIMenu and UIAction


UIMenu and UIAction

Hierarchical menu construction system

UIMenus are composable


UIMenu and UIAction

UIMenu

UIAction(“Share”)

UIAction(“Delete”)

UIMenu and UIAction

UIMenu

UIAction(“Share”)

UIMenu(“Edit…”)

UIAction(“Copy”)

UIAction(“Duplicate”)

UIAction(“Delete”)
// Create a UIContextMenuInteraction with Some Delegate
let interaction = UIContextMenuInteraction(delegate: self)

// Attach It to Our View


menuSourceView.addInteraction(interaction)

UIContextMenuInteractionDelegate

func contextMenuInteraction(_ interaction: UIContextMenuInteraction,


configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration?

One required method

Return a UIContextMenuConfiguration to start

Return nil to prevent interaction


UIContextMenuConfiguration

class UIContextMenuConfiguration: NSObject {

// ...

convenience init(identifier: NSCopying?,


previewProvider: UIContextMenuContentPreviewProvider?,
actionProvider: UIContextMenuActionProvider?)
}
let actionProvider = (suggestedActions: [UIMenuElement]) -> UIMenu? {

let editMenu = UIMenu(title: "Edit…", children: [


UIAction(title: "Copy") { ... },
UIAction(title: "Duplicate") { ... }
])

return UIMenu(children: [
UIAction(title: "Share") { ... },
editMenu,
UIAction(title: "Delete", style: .destructive) { ... }
])
}.

return UIContextMenuConfiguration(identifier: "unique-ID" as NSCopying,


previewProvider: nil,
actionProvider: actionProvider)
let actionProvider = (suggestedActions: [UIMenuElement]) -> UIMenu? {

let editMenu = UIMenu(title: "Edit…", children: [


UIAction(title: "Copy") { ... },
UIAction(title: "Duplicate") { ... }
])

return UIMenu(children: [
UIAction(title: "Share") { ... },
editMenu,
UIAction(title: "Delete", style: .destructive) { ... }
])
}.

return UIContextMenuConfiguration(identifier: "unique-ID" as NSCopying,


previewProvider: nil,
actionProvider: actionProvider)
let actionProvider = (suggestedActions: [UIMenuElement]) -> UIMenu? {

let editMenu = UIMenu(title: "Edit…", children: [


UIAction(title: "Copy") { ... },
UIAction(title: "Duplicate") { ... }
])

return UIMenu(children: [
UIAction(title: "Share") { ... },
editMenu,
UIAction(title: "Delete", style: .destructive) { ... }
])
}.

return UIContextMenuConfiguration(identifier: "unique-ID" as NSCopying,


previewProvider: nil,
actionProvider: actionProvider)
let actionProvider = (suggestedActions: [UIMenuElement]) -> UIMenu? {

let editMenu = UIMenu(title: "Edit…", children: [


UIAction(title: "Copy") { ... },
UIAction(title: "Duplicate") { ... }
])

return UIMenu(children: [
UIAction(title: "Share") { ... },
editMenu,
UIAction(title: "Delete", style: .destructive) { ... }
])
}.

return UIContextMenuConfiguration(identifier: "unique-ID" as NSCopying,


previewProvider: nil,
actionProvider: actionProvider)

Above and Beyond

Animation customization

Set source and destination

UITargetedPreview and UITargetedDragPreview


Above and Beyond

Animation customization

Set source and destination

UITargetedPreview and UITargetedDragPreview


UITableView and UICollectionView

// UITableViewDelegate
optional func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAtIndexPath
indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?

UITableView and UICollectionView

// UITableViewDelegate
optional func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAtIndexPath
indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?

Polished default animations

Custom presentation and dismiss


Peek and.Pop

UIViewControllerPreviewing
Peek and.Pop

UIViewControllerPreviewing

UIContextMenuInteraction
Updating Your App

Replace long-press driven menus

Adopt it alongside Drag and Drop


Ready to Launch

Make your app flexible

Give your app a modern look

Build a great search experience

Make your app powerful


More Information
developer.apple.com/wwdc19/224

You might also like