3D Touch
Karol Kozub
2016-02-22
The device senses how much pressure you apply to the display
“An entirely new way to interact with your phone”
UITouch.force
public class UITouch : NSObject {
// …
// Force of the touch, where 1.0 represents the force of an average touch
public var force: CGFloat { get }
// Maximum possible force with this input mechanism
public var maximumPossibleForce: CGFloat { get }
// …
}
UITouch.force
public class UITouch : NSObject {
// …
// Force of the touch, where 1.0 represents the force of an average touch
public var force: CGFloat { get }
// Maximum possible force with this input mechanism
public var maximumPossibleForce: CGFloat { get }
// …
}
For some reason equal to 6.66…
on the iPhone 6s
UITouch.force
public class UITouch : NSObject {
// …
// Force of the touch, where 1.0 represents the force of an average touch
public var force: CGFloat { get }
// Maximum possible force with this input mechanism
public var maximumPossibleForce: CGFloat { get }
// …
}
Quick Actions
• Apps can have up to 4
• Added either through Info.plist
• Or dynamically in code
Quick Actions
Static Quick Actions
Static Quick Actions
Required
Static Quick Actions
Optional
Static Quick Actions
Type
Static Quick Actions
Title
Static Quick Actions
Subtitle
Static Quick Actions
Icon
Static Quick Actions
Icon
Static Quick Actions
User Info
System Icon Types
iOS 9.0
iOS 9.1
Dynamic Quick Actions
extension UIApplication {
// Register shortcuts to display on the home screen, or retrieve currently registered shortcuts.
@available(iOS 9.0, *)
public var shortcutItems: [UIApplicationShortcutItem]?
}
Dynamic Quick Actions
@available(iOS 9.0, *)
public class UIApplicationShortcutItem : NSObject, NSCopying, NSMutableCopying {
public init(type: String,
localizedTitle: String,
localizedSubtitle: String?,
icon: UIApplicationShortcutIcon?,
userInfo: [NSObject : AnyObject]?)
public convenience init(type: String,
localizedTitle: String)
// An application-specific string that identifies the type of action to perform.
public var type: String { get }
// Properties controlling how the item should be displayed on the home screen.
public var localizedTitle: String { get }
public var localizedSubtitle: String? { get }
@NSCopying public var icon: UIApplicationShortcutIcon? { get }
// Application-specific information needed to perform the action.
// Will throw an exception if the NSDictionary is not plist-encodable.
public var userInfo: [String : NSSecureCoding]? { get }
}
extension UIApplication {
// Register shortcuts to display on the home screen, or retrieve currently registered shortcuts.
@available(iOS 9.0, *)
public var shortcutItems: [UIApplicationShortcutItem]?
}
Dynamic Quick Actions
@available(iOS 9.0, *)
public class UIApplicationShortcutItem : NSObject, NSCopying, NSMutableCopying {
public init(type: String,
localizedTitle: String,
localizedSubtitle: String?,
icon: UIApplicationShortcutIcon?,
userInfo: [NSObject : AnyObject]?)
public convenience init(type: String,
localizedTitle: String)
// An application-specific string that identifies the type of action to perform.
public var type: String { get }
// Properties controlling how the item should be displayed on the home screen.
public var localizedTitle: String { get }
public var localizedSubtitle: String? { get }
@NSCopying public var icon: UIApplicationShortcutIcon? { get }
// Application-specific information needed to perform the action.
// Will throw an exception if the NSDictionary is not plist-encodable.
public var userInfo: [String : NSSecureCoding]? { get }
}
extension UIApplication {
// Register shortcuts to display on the home screen, or retrieve currently registered shortcuts.
@available(iOS 9.0, *)
public var shortcutItems: [UIApplicationShortcutItem]?
}
Required
Optional
Dynamic Quick Actions
let iconTypeIcon = UIApplicationShortcutIcon(type: .Search)
let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item",
localizedTitle: "Search",
localizedSubtitle: "icon from an icon type",
icon: iconTypeIcon,
userInfo: nil)
let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle")
let templateItem = UIApplicationShortcutItem(type: "template-item",
localizedTitle: "Triangle",
localizedSubtitle: "icon from template image",
icon: templateIcon,
userInfo: nil)
let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore)
let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item",
localizedTitle: "Karol",
localizedSubtitle: "icon from real contact",
icon: contactStoreContactIcon,
userInfo: nil)
let temporaryContact = CNMutableContact()
temporaryContact.givenName = "Hiro";
temporaryContact.familyName = "Protagonist";
// this will be ignored
temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!)
let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact)
let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item",
localizedTitle: "Hiro",
localizedSubtitle: "icon from tmp contact",
icon: temporaryContactIcon,
userInfo: nil)
UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem,
contactStoreContactItem, temporaryContactItem]
Dynamic Quick Actions
let iconTypeIcon = UIApplicationShortcutIcon(type: .Search)
let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item",
localizedTitle: "Search",
localizedSubtitle: "icon from an icon type",
icon: iconTypeIcon,
userInfo: nil)
let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle")
let templateItem = UIApplicationShortcutItem(type: "template-item",
localizedTitle: "Triangle",
localizedSubtitle: "icon from template image",
icon: templateIcon,
userInfo: nil)
let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore)
let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item",
localizedTitle: "Karol",
localizedSubtitle: "icon from real contact",
icon: contactStoreContactIcon,
userInfo: nil)
let temporaryContact = CNMutableContact()
temporaryContact.givenName = "Hiro";
temporaryContact.familyName = "Protagonist";
// this will be ignored
temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!)
let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact)
let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item",
localizedTitle: "Hiro",
localizedSubtitle: "icon from tmp contact",
icon: temporaryContactIcon,
userInfo: nil)
UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem,
contactStoreContactItem, temporaryContactItem]
Dynamic Quick Actions
let iconTypeIcon = UIApplicationShortcutIcon(type: .Search)
let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item",
localizedTitle: "Search",
localizedSubtitle: "icon from an icon type",
icon: iconTypeIcon,
userInfo: nil)
let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle")
let templateItem = UIApplicationShortcutItem(type: "template-item",
localizedTitle: "Triangle",
localizedSubtitle: "icon from template image",
icon: templateIcon,
userInfo: nil)
let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore)
let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item",
localizedTitle: "Karol",
localizedSubtitle: "icon from real contact",
icon: contactStoreContactIcon,
userInfo: nil)
let temporaryContact = CNMutableContact()
temporaryContact.givenName = "Hiro";
temporaryContact.familyName = "Protagonist";
// this will be ignored
temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!)
let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact)
let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item",
localizedTitle: "Hiro",
localizedSubtitle: "icon from tmp contact",
icon: temporaryContactIcon,
userInfo: nil)
UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem,
contactStoreContactItem, temporaryContactItem]
Dynamic Quick Actions
let iconTypeIcon = UIApplicationShortcutIcon(type: .Search)
let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item",
localizedTitle: "Search",
localizedSubtitle: "icon from an icon type",
icon: iconTypeIcon,
userInfo: nil)
let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle")
let templateItem = UIApplicationShortcutItem(type: "template-item",
localizedTitle: "Triangle",
localizedSubtitle: "icon from template image",
icon: templateIcon,
userInfo: nil)
let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore)
let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item",
localizedTitle: "Karol",
localizedSubtitle: "icon from real contact",
icon: contactStoreContactIcon,
userInfo: nil)
let temporaryContact = CNMutableContact()
temporaryContact.givenName = "Hiro";
temporaryContact.familyName = "Protagonist";
// this will be ignored
temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!)
let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact)
let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item",
localizedTitle: "Hiro",
localizedSubtitle: "icon from tmp contact",
icon: temporaryContactIcon,
userInfo: nil)
UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem,
contactStoreContactItem, temporaryContactItem]
Dynamic Quick Actions
let iconTypeIcon = UIApplicationShortcutIcon(type: .Search)
let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item",
localizedTitle: "Search",
localizedSubtitle: "icon from an icon type",
icon: iconTypeIcon,
userInfo: nil)
let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle")
let templateItem = UIApplicationShortcutItem(type: "template-item",
localizedTitle: "Triangle",
localizedSubtitle: "icon from template image",
icon: templateIcon,
userInfo: nil)
let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore)
let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item",
localizedTitle: "Karol",
localizedSubtitle: "icon from real contact",
icon: contactStoreContactIcon,
userInfo: nil)
let temporaryContact = CNMutableContact()
temporaryContact.givenName = "Hiro";
temporaryContact.familyName = "Protagonist";
// this will be ignored
temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!)
let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact)
let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item",
localizedTitle: "Hiro",
localizedSubtitle: "icon from tmp contact",
icon: temporaryContactIcon,
userInfo: nil)
UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem,
contactStoreContactItem, temporaryContactItem]
Dynamic Quick Actions
let iconTypeIcon = UIApplicationShortcutIcon(type: .Search)
let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item",
localizedTitle: "Search",
localizedSubtitle: "icon from an icon type",
icon: iconTypeIcon,
userInfo: nil)
let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle")
let templateItem = UIApplicationShortcutItem(type: "template-item",
localizedTitle: "Triangle",
localizedSubtitle: "icon from template image",
icon: templateIcon,
userInfo: nil)
let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore)
let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item",
localizedTitle: "Karol",
localizedSubtitle: "icon from real contact",
icon: contactStoreContactIcon,
userInfo: nil)
let temporaryContact = CNMutableContact()
temporaryContact.givenName = "Hiro";
temporaryContact.familyName = "Protagonist";
// this will be ignored
temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!)
let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact)
let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item",
localizedTitle: "Hiro",
localizedSubtitle: "icon from tmp contact",
icon: temporaryContactIcon,
userInfo: nil)
UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem,
contactStoreContactItem, temporaryContactItem]
Handling Quick Actions
public protocol UIApplicationDelegate : NSObjectProtocol {
// …
// Called when the user activates your application by
// selecting a shortcut on the home screen,
// except when -application:willFinishLaunchingWithOptions: or
// -application:didFinishLaunchingWithOptions returns NO.
@available(iOS 9.0, *)
optional public func application(application: UIApplication,
performActionForShortcutItem shortcutItem: UIApplicationShortcutItem,
completionHandler: (Bool) -> Void)
// …
}
Handling Quick Actions
public protocol UIApplicationDelegate : NSObjectProtocol {
// …
// Called when the user activates your application by
// selecting a shortcut on the home screen,
// except when -application:willFinishLaunchingWithOptions: or
// -application:didFinishLaunchingWithOptions returns NO.
@available(iOS 9.0, *)
optional public func application(application: UIApplication,
performActionForShortcutItem shortcutItem: UIApplicationShortcutItem,
completionHandler: (Bool) -> Void)
// …
}
Full-blown shortcut item
Handling Quick Actions
public protocol UIApplicationDelegate : NSObjectProtocol {
// …
// Called when the user activates your application by
// selecting a shortcut on the home screen,
// except when -application:willFinishLaunchingWithOptions: or
// -application:didFinishLaunchingWithOptions returns NO.
@available(iOS 9.0, *)
optional public func application(application: UIApplication,
performActionForShortcutItem shortcutItem: UIApplicationShortcutItem,
completionHandler: (Bool) -> Void)
// …
}
Call when finished
Peek and Pop
• Press to Peek at what’s inside
• Stop pressing to hide the preview
• Press harder to Pop into the view
Peek and Pop
Peek and Pop
extension UIViewController {
// Registers a view controller to participate with 3D Touch preview (peek) and commit (pop).
@available(iOS 9.0, *)
public func registerForPreviewingWithDelegate(delegate: UIViewControllerPreviewingDelegate,
sourceView: UIView) -> UIViewControllerPreviewing
@available(iOS 9.0, *)
public func unregisterForPreviewingWithContext(previewing: UIViewControllerPreviewing)
}
@available(iOS 9.0, *)
public protocol UIViewControllerPreviewingDelegate : NSObjectProtocol {
// If you return nil, a preview presentation will not be performed
@available(iOS 9.0, *)
public func previewingContext(previewingContext: UIViewControllerPreviewing,
viewControllerForLocation location: CGPoint) -> UIViewController?
@available(iOS 9.0, *)
public func previewingContext(previewingContext: UIViewControllerPreviewing,
commitViewController viewControllerToCommit: UIViewController)
}
Peek and Pop
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
@IBOutlet weak var sourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
registerForPreviewingWithDelegate(self, sourceView: sourceView)
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
viewControllerForLocation location: CGPoint) -> UIViewController? {
return PreviewedViewController()
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
commitViewController viewControllerToCommit: UIViewController) {
navigationController?.pushViewController(viewControllerToCommit,
animated: false)
}
}
Peek and Pop
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
@IBOutlet weak var sourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
registerForPreviewingWithDelegate(self, sourceView: sourceView)
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
viewControllerForLocation location: CGPoint) -> UIViewController? {
return PreviewedViewController()
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
commitViewController viewControllerToCommit: UIViewController) {
navigationController?.pushViewController(viewControllerToCommit,
animated: false)
}
}
Peek and Pop
public protocol UIViewControllerPreviewing : NSObjectProtocol {
// This gesture can be used to cause the previewing presentation
// to wait until one of your gestures fails or to allow simultaneous
// recognition during the initial phase of the preview presentation.
@available(iOS 9.0, *)
public var previewingGestureRecognizerForFailureRelationship: UIGestureRecognizer { get }
@available(iOS 9.0, *)
public var delegate: UIViewControllerPreviewingDelegate { get }
@available(iOS 9.0, *)
public var sourceView: UIView { get }
// This rect will be set to the bounds of sourceView before each call to
// -previewingContext:viewControllerForLocation:
@available(iOS 9.0, *)
public var sourceRect: CGRect { get set }
}
Peek and Pop
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
@IBOutlet weak var sourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
registerForPreviewingWithDelegate(self, sourceView: sourceView)
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
viewControllerForLocation location: CGPoint) -> UIViewController? {
let rect = CGRectInset(previewingContext.sourceView.bounds, 20, 20)
previewingContext.sourceRect = rect
return PreviewedViewController()
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
commitViewController viewControllerToCommit: UIViewController) {
navigationController?.pushViewController(viewControllerToCommit,
animated: false)
}
}
Peek and Pop
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
@IBOutlet weak var sourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
registerForPreviewingWithDelegate(self, sourceView: sourceView)
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
viewControllerForLocation location: CGPoint) -> UIViewController? {
return nil
}
func previewingContext(previewingContext: UIViewControllerPreviewing,
commitViewController viewControllerToCommit: UIViewController) {
navigationController?.pushViewController(viewControllerToCommit,
animated: false)
}
}
Peek Quick Actions
extension UIViewController {
public func previewActionItems() -> [UIPreviewActionItem]
}
public protocol UIPreviewActionItem : NSObjectProtocol {
public var title: String { get }
}
public class UIPreviewAction : NSObject, NSCopying, UIPreviewActionItem {
public var handler: (UIPreviewActionItem, UIViewController) -> Void { get }
public convenience init(title: String,
style: UIPreviewActionStyle,
handler: (UIPreviewAction, UIViewController) -> Void)
}
public class UIPreviewActionGroup : NSObject, NSCopying, UIPreviewActionItem {
public convenience init(title: String,
style: UIPreviewActionStyle,
actions: [UIPreviewAction])
}
Peek Quick Actions
extension UIViewController {
public func previewActionItems() -> [UIPreviewActionItem]
}
public protocol UIPreviewActionItem : NSObjectProtocol {
public var title: String { get }
}
public class UIPreviewAction : NSObject, NSCopying, UIPreviewActionItem {
public var handler: (UIPreviewActionItem, UIViewController) -> Void { get }
public convenience init(title: String,
style: UIPreviewActionStyle,
handler: (UIPreviewAction, UIViewController) -> Void)
}
public class UIPreviewActionGroup : NSObject, NSCopying, UIPreviewActionItem {
public convenience init(title: String,
style: UIPreviewActionStyle,
actions: [UIPreviewAction])
}
Peek Quick Actions
extension UIViewController {
public func previewActionItems() -> [UIPreviewActionItem]
}
public protocol UIPreviewActionItem : NSObjectProtocol {
public var title: String { get }
}
public class UIPreviewAction : NSObject, NSCopying, UIPreviewActionItem {
public var handler: (UIPreviewActionItem, UIViewController) -> Void { get }
public convenience init(title: String,
style: UIPreviewActionStyle,
handler: (UIPreviewAction, UIViewController) -> Void)
}
public class UIPreviewActionGroup : NSObject, NSCopying, UIPreviewActionItem {
public convenience init(title: String,
style: UIPreviewActionStyle,
actions: [UIPreviewAction])
}
Peek Quick Actions
class PreviewedViewController: UIViewController {
override func previewActionItems() -> [UIPreviewActionItem] {
let defaultStyleAction = UIPreviewAction(title: "Default Style",
style: .Default) {action, controller in
}
let destructiveStyleAction = UIPreviewAction(title: "Destructive Style",
style: .Destructive) {action, controller in
}
let selectedStyleAction = UIPreviewAction(title: "Selected Style",
style: .Selected) {action, controller in
}
let actionGroup = UIPreviewActionGroup(title: "Group",
style: .Default,
actions: [defaultStyleAction, defaultStyleAction, defaultStyleAction])
return [defaultStyleAction, destructiveStyleAction,
selectedStyleAction, actionGroup]
}
}
Peek Quick Actions
class PreviewedViewController: UIViewController {
override func previewActionItems() -> [UIPreviewActionItem] {
let defaultStyleAction = UIPreviewAction(title: "Default Style",
style: .Default) {action, controller in
}
let destructiveStyleAction = UIPreviewAction(title: "Destructive Style",
style: .Destructive) {action, controller in
}
let selectedStyleAction = UIPreviewAction(title: "Selected Style",
style: .Selected) {action, controller in
}
return [defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction,
defaultStyleAction, destructiveStyleAction, selectedStyleAction]
}
}
Detecting 3D Touch
public class UITraitCollection : NSObject, NSCopying, NSSecureCoding {
// …
public var forceTouchCapability: UIForceTouchCapability { get }
// …
}
public enum UIForceTouchCapability : Int {
case Unknown
case Unavailable
case Available
}
Detecting 3D Touch
public class UITraitCollection : NSObject, NSCopying, NSSecureCoding {
// …
public var forceTouchCapability: UIForceTouchCapability { get }
// …
}
public enum UIForceTouchCapability : Int {
case Unknown
case Unavailable
case Available
}
/*! Trait environments expose a trait collection that describes their environment. */
public protocol UITraitEnvironment : NSObjectProtocol {
public var traitCollection: UITraitCollection { get }
/*! To be overridden as needed to provide
custom behavior when the environment's traits change. */
public func traitCollectionDidChange(previousTraitCollection: UITraitCollection?)
}
Detecting 3D Touch
public class UITraitCollection : NSObject, NSCopying, NSSecureCoding {
// …
public var forceTouchCapability: UIForceTouchCapability { get }
// …
}
public enum UIForceTouchCapability : Int {
case Unknown
case Unavailable
case Available
}
/*! Trait environments expose a trait collection that describes their environment. */
public protocol UITraitEnvironment : NSObjectProtocol {
public var traitCollection: UITraitCollection { get }
/*! To be overridden as needed to provide
custom behavior when the environment's traits change. */
public func traitCollectionDidChange(previousTraitCollection: UITraitCollection?)
}
The following interface classes adopt this protocol:
UIScreen, UIWindow, UIViewController, UIPresentationController, and UIView.
Summary
• UITouch.force
• Static Quick Actions
• Dynamic Quick Actions
• Peek and Pop
• Peek Quick Actions
• Detecting 3D Touch
Summary
• UITouch.force
• Static Quick Actions
• Dynamic Quick Actions
• Peek and Pop
• Peek Quick Actions
• Detecting 3D Touch
Summary
• UITouch.force
• Static Quick Actions
• Dynamic Quick Actions
• Peek and Pop
• Peek Quick Actions
• Detecting 3D Touch
Summary
• UITouch.force
• Static Quick Actions
• Dynamic Quick Actions
• Peek and Pop
• Peek Quick Actions
• Detecting 3D Touch
Summary
• UITouch.force
• Static Quick Actions
• Dynamic Quick Actions
• Peek and Pop
• Peek Quick Actions
• Detecting 3D Touch
Summary
• UITouch.force
• Static Quick Actions
• Dynamic Quick Actions
• Peek and Pop
• Peek Quick Actions
• Detecting 3D Touch
Thank you for listening

3D Touch by Karol Kozub, Macoscope

  • 1.
  • 2.
    The device senseshow much pressure you apply to the display
  • 3.
    “An entirely newway to interact with your phone”
  • 4.
    UITouch.force public class UITouch: NSObject { // … // Force of the touch, where 1.0 represents the force of an average touch public var force: CGFloat { get } // Maximum possible force with this input mechanism public var maximumPossibleForce: CGFloat { get } // … }
  • 5.
    UITouch.force public class UITouch: NSObject { // … // Force of the touch, where 1.0 represents the force of an average touch public var force: CGFloat { get } // Maximum possible force with this input mechanism public var maximumPossibleForce: CGFloat { get } // … } For some reason equal to 6.66… on the iPhone 6s
  • 6.
    UITouch.force public class UITouch: NSObject { // … // Force of the touch, where 1.0 represents the force of an average touch public var force: CGFloat { get } // Maximum possible force with this input mechanism public var maximumPossibleForce: CGFloat { get } // … }
  • 7.
  • 8.
    • Apps canhave up to 4 • Added either through Info.plist • Or dynamically in code Quick Actions
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
    Dynamic Quick Actions extensionUIApplication { // Register shortcuts to display on the home screen, or retrieve currently registered shortcuts. @available(iOS 9.0, *) public var shortcutItems: [UIApplicationShortcutItem]? }
  • 20.
    Dynamic Quick Actions @available(iOS9.0, *) public class UIApplicationShortcutItem : NSObject, NSCopying, NSMutableCopying { public init(type: String, localizedTitle: String, localizedSubtitle: String?, icon: UIApplicationShortcutIcon?, userInfo: [NSObject : AnyObject]?) public convenience init(type: String, localizedTitle: String) // An application-specific string that identifies the type of action to perform. public var type: String { get } // Properties controlling how the item should be displayed on the home screen. public var localizedTitle: String { get } public var localizedSubtitle: String? { get } @NSCopying public var icon: UIApplicationShortcutIcon? { get } // Application-specific information needed to perform the action. // Will throw an exception if the NSDictionary is not plist-encodable. public var userInfo: [String : NSSecureCoding]? { get } } extension UIApplication { // Register shortcuts to display on the home screen, or retrieve currently registered shortcuts. @available(iOS 9.0, *) public var shortcutItems: [UIApplicationShortcutItem]? }
  • 21.
    Dynamic Quick Actions @available(iOS9.0, *) public class UIApplicationShortcutItem : NSObject, NSCopying, NSMutableCopying { public init(type: String, localizedTitle: String, localizedSubtitle: String?, icon: UIApplicationShortcutIcon?, userInfo: [NSObject : AnyObject]?) public convenience init(type: String, localizedTitle: String) // An application-specific string that identifies the type of action to perform. public var type: String { get } // Properties controlling how the item should be displayed on the home screen. public var localizedTitle: String { get } public var localizedSubtitle: String? { get } @NSCopying public var icon: UIApplicationShortcutIcon? { get } // Application-specific information needed to perform the action. // Will throw an exception if the NSDictionary is not plist-encodable. public var userInfo: [String : NSSecureCoding]? { get } } extension UIApplication { // Register shortcuts to display on the home screen, or retrieve currently registered shortcuts. @available(iOS 9.0, *) public var shortcutItems: [UIApplicationShortcutItem]? } Required Optional
  • 22.
    Dynamic Quick Actions leticonTypeIcon = UIApplicationShortcutIcon(type: .Search) let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item", localizedTitle: "Search", localizedSubtitle: "icon from an icon type", icon: iconTypeIcon, userInfo: nil) let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle") let templateItem = UIApplicationShortcutItem(type: "template-item", localizedTitle: "Triangle", localizedSubtitle: "icon from template image", icon: templateIcon, userInfo: nil) let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore) let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item", localizedTitle: "Karol", localizedSubtitle: "icon from real contact", icon: contactStoreContactIcon, userInfo: nil) let temporaryContact = CNMutableContact() temporaryContact.givenName = "Hiro"; temporaryContact.familyName = "Protagonist"; // this will be ignored temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!) let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact) let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item", localizedTitle: "Hiro", localizedSubtitle: "icon from tmp contact", icon: temporaryContactIcon, userInfo: nil) UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem, contactStoreContactItem, temporaryContactItem]
  • 23.
    Dynamic Quick Actions leticonTypeIcon = UIApplicationShortcutIcon(type: .Search) let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item", localizedTitle: "Search", localizedSubtitle: "icon from an icon type", icon: iconTypeIcon, userInfo: nil) let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle") let templateItem = UIApplicationShortcutItem(type: "template-item", localizedTitle: "Triangle", localizedSubtitle: "icon from template image", icon: templateIcon, userInfo: nil) let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore) let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item", localizedTitle: "Karol", localizedSubtitle: "icon from real contact", icon: contactStoreContactIcon, userInfo: nil) let temporaryContact = CNMutableContact() temporaryContact.givenName = "Hiro"; temporaryContact.familyName = "Protagonist"; // this will be ignored temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!) let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact) let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item", localizedTitle: "Hiro", localizedSubtitle: "icon from tmp contact", icon: temporaryContactIcon, userInfo: nil) UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem, contactStoreContactItem, temporaryContactItem]
  • 24.
    Dynamic Quick Actions leticonTypeIcon = UIApplicationShortcutIcon(type: .Search) let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item", localizedTitle: "Search", localizedSubtitle: "icon from an icon type", icon: iconTypeIcon, userInfo: nil) let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle") let templateItem = UIApplicationShortcutItem(type: "template-item", localizedTitle: "Triangle", localizedSubtitle: "icon from template image", icon: templateIcon, userInfo: nil) let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore) let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item", localizedTitle: "Karol", localizedSubtitle: "icon from real contact", icon: contactStoreContactIcon, userInfo: nil) let temporaryContact = CNMutableContact() temporaryContact.givenName = "Hiro"; temporaryContact.familyName = "Protagonist"; // this will be ignored temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!) let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact) let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item", localizedTitle: "Hiro", localizedSubtitle: "icon from tmp contact", icon: temporaryContactIcon, userInfo: nil) UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem, contactStoreContactItem, temporaryContactItem]
  • 25.
    Dynamic Quick Actions leticonTypeIcon = UIApplicationShortcutIcon(type: .Search) let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item", localizedTitle: "Search", localizedSubtitle: "icon from an icon type", icon: iconTypeIcon, userInfo: nil) let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle") let templateItem = UIApplicationShortcutItem(type: "template-item", localizedTitle: "Triangle", localizedSubtitle: "icon from template image", icon: templateIcon, userInfo: nil) let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore) let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item", localizedTitle: "Karol", localizedSubtitle: "icon from real contact", icon: contactStoreContactIcon, userInfo: nil) let temporaryContact = CNMutableContact() temporaryContact.givenName = "Hiro"; temporaryContact.familyName = "Protagonist"; // this will be ignored temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!) let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact) let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item", localizedTitle: "Hiro", localizedSubtitle: "icon from tmp contact", icon: temporaryContactIcon, userInfo: nil) UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem, contactStoreContactItem, temporaryContactItem]
  • 26.
    Dynamic Quick Actions leticonTypeIcon = UIApplicationShortcutIcon(type: .Search) let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item", localizedTitle: "Search", localizedSubtitle: "icon from an icon type", icon: iconTypeIcon, userInfo: nil) let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle") let templateItem = UIApplicationShortcutItem(type: "template-item", localizedTitle: "Triangle", localizedSubtitle: "icon from template image", icon: templateIcon, userInfo: nil) let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore) let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item", localizedTitle: "Karol", localizedSubtitle: "icon from real contact", icon: contactStoreContactIcon, userInfo: nil) let temporaryContact = CNMutableContact() temporaryContact.givenName = "Hiro"; temporaryContact.familyName = "Protagonist"; // this will be ignored temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!) let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact) let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item", localizedTitle: "Hiro", localizedSubtitle: "icon from tmp contact", icon: temporaryContactIcon, userInfo: nil) UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem, contactStoreContactItem, temporaryContactItem]
  • 27.
    Dynamic Quick Actions leticonTypeIcon = UIApplicationShortcutIcon(type: .Search) let iconTypeItem = UIApplicationShortcutItem(type: "icon-type-item", localizedTitle: "Search", localizedSubtitle: "icon from an icon type", icon: iconTypeIcon, userInfo: nil) let templateIcon = UIApplicationShortcutIcon(templateImageName: "triangle") let templateItem = UIApplicationShortcutItem(type: "template-item", localizedTitle: "Triangle", localizedSubtitle: "icon from template image", icon: templateIcon, userInfo: nil) let contactStoreContactIcon = UIApplicationShortcutIcon(contact: contactFromContactStore) let contactStoreContactItem = UIApplicationShortcutItem(type: "real-contact-item", localizedTitle: "Karol", localizedSubtitle: "icon from real contact", icon: contactStoreContactIcon, userInfo: nil) let temporaryContact = CNMutableContact() temporaryContact.givenName = "Hiro"; temporaryContact.familyName = "Protagonist"; // this will be ignored temporaryContact.imageData = UIImagePNGRepresentation(UIImage(named: "triangle")!) let temporaryContactIcon = UIApplicationShortcutIcon(contact: temporaryContact) let temporaryContactItem = UIApplicationShortcutItem(type: "temporary-contact-item", localizedTitle: "Hiro", localizedSubtitle: "icon from tmp contact", icon: temporaryContactIcon, userInfo: nil) UIApplication.sharedApplication().shortcutItems = [iconTypeItem, templateItem, contactStoreContactItem, temporaryContactItem]
  • 28.
    Handling Quick Actions publicprotocol UIApplicationDelegate : NSObjectProtocol { // … // Called when the user activates your application by // selecting a shortcut on the home screen, // except when -application:willFinishLaunchingWithOptions: or // -application:didFinishLaunchingWithOptions returns NO. @available(iOS 9.0, *) optional public func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) // … }
  • 29.
    Handling Quick Actions publicprotocol UIApplicationDelegate : NSObjectProtocol { // … // Called when the user activates your application by // selecting a shortcut on the home screen, // except when -application:willFinishLaunchingWithOptions: or // -application:didFinishLaunchingWithOptions returns NO. @available(iOS 9.0, *) optional public func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) // … } Full-blown shortcut item
  • 30.
    Handling Quick Actions publicprotocol UIApplicationDelegate : NSObjectProtocol { // … // Called when the user activates your application by // selecting a shortcut on the home screen, // except when -application:willFinishLaunchingWithOptions: or // -application:didFinishLaunchingWithOptions returns NO. @available(iOS 9.0, *) optional public func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) // … } Call when finished
  • 31.
  • 32.
    • Press toPeek at what’s inside • Stop pressing to hide the preview • Press harder to Pop into the view Peek and Pop
  • 33.
    Peek and Pop extensionUIViewController { // Registers a view controller to participate with 3D Touch preview (peek) and commit (pop). @available(iOS 9.0, *) public func registerForPreviewingWithDelegate(delegate: UIViewControllerPreviewingDelegate, sourceView: UIView) -> UIViewControllerPreviewing @available(iOS 9.0, *) public func unregisterForPreviewingWithContext(previewing: UIViewControllerPreviewing) } @available(iOS 9.0, *) public protocol UIViewControllerPreviewingDelegate : NSObjectProtocol { // If you return nil, a preview presentation will not be performed @available(iOS 9.0, *) public func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? @available(iOS 9.0, *) public func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) }
  • 34.
    Peek and Pop classViewController: UIViewController, UIViewControllerPreviewingDelegate { @IBOutlet weak var sourceView: UIView! override func viewDidLoad() { super.viewDidLoad() registerForPreviewingWithDelegate(self, sourceView: sourceView) } func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { return PreviewedViewController() } func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { navigationController?.pushViewController(viewControllerToCommit, animated: false) } }
  • 35.
    Peek and Pop classViewController: UIViewController, UIViewControllerPreviewingDelegate { @IBOutlet weak var sourceView: UIView! override func viewDidLoad() { super.viewDidLoad() registerForPreviewingWithDelegate(self, sourceView: sourceView) } func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { return PreviewedViewController() } func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { navigationController?.pushViewController(viewControllerToCommit, animated: false) } }
  • 36.
    Peek and Pop publicprotocol UIViewControllerPreviewing : NSObjectProtocol { // This gesture can be used to cause the previewing presentation // to wait until one of your gestures fails or to allow simultaneous // recognition during the initial phase of the preview presentation. @available(iOS 9.0, *) public var previewingGestureRecognizerForFailureRelationship: UIGestureRecognizer { get } @available(iOS 9.0, *) public var delegate: UIViewControllerPreviewingDelegate { get } @available(iOS 9.0, *) public var sourceView: UIView { get } // This rect will be set to the bounds of sourceView before each call to // -previewingContext:viewControllerForLocation: @available(iOS 9.0, *) public var sourceRect: CGRect { get set } }
  • 37.
    Peek and Pop classViewController: UIViewController, UIViewControllerPreviewingDelegate { @IBOutlet weak var sourceView: UIView! override func viewDidLoad() { super.viewDidLoad() registerForPreviewingWithDelegate(self, sourceView: sourceView) } func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { let rect = CGRectInset(previewingContext.sourceView.bounds, 20, 20) previewingContext.sourceRect = rect return PreviewedViewController() } func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { navigationController?.pushViewController(viewControllerToCommit, animated: false) } }
  • 38.
    Peek and Pop classViewController: UIViewController, UIViewControllerPreviewingDelegate { @IBOutlet weak var sourceView: UIView! override func viewDidLoad() { super.viewDidLoad() registerForPreviewingWithDelegate(self, sourceView: sourceView) } func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { return nil } func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { navigationController?.pushViewController(viewControllerToCommit, animated: false) } }
  • 39.
    Peek Quick Actions extensionUIViewController { public func previewActionItems() -> [UIPreviewActionItem] } public protocol UIPreviewActionItem : NSObjectProtocol { public var title: String { get } } public class UIPreviewAction : NSObject, NSCopying, UIPreviewActionItem { public var handler: (UIPreviewActionItem, UIViewController) -> Void { get } public convenience init(title: String, style: UIPreviewActionStyle, handler: (UIPreviewAction, UIViewController) -> Void) } public class UIPreviewActionGroup : NSObject, NSCopying, UIPreviewActionItem { public convenience init(title: String, style: UIPreviewActionStyle, actions: [UIPreviewAction]) }
  • 40.
    Peek Quick Actions extensionUIViewController { public func previewActionItems() -> [UIPreviewActionItem] } public protocol UIPreviewActionItem : NSObjectProtocol { public var title: String { get } } public class UIPreviewAction : NSObject, NSCopying, UIPreviewActionItem { public var handler: (UIPreviewActionItem, UIViewController) -> Void { get } public convenience init(title: String, style: UIPreviewActionStyle, handler: (UIPreviewAction, UIViewController) -> Void) } public class UIPreviewActionGroup : NSObject, NSCopying, UIPreviewActionItem { public convenience init(title: String, style: UIPreviewActionStyle, actions: [UIPreviewAction]) }
  • 41.
    Peek Quick Actions extensionUIViewController { public func previewActionItems() -> [UIPreviewActionItem] } public protocol UIPreviewActionItem : NSObjectProtocol { public var title: String { get } } public class UIPreviewAction : NSObject, NSCopying, UIPreviewActionItem { public var handler: (UIPreviewActionItem, UIViewController) -> Void { get } public convenience init(title: String, style: UIPreviewActionStyle, handler: (UIPreviewAction, UIViewController) -> Void) } public class UIPreviewActionGroup : NSObject, NSCopying, UIPreviewActionItem { public convenience init(title: String, style: UIPreviewActionStyle, actions: [UIPreviewAction]) }
  • 42.
    Peek Quick Actions classPreviewedViewController: UIViewController { override func previewActionItems() -> [UIPreviewActionItem] { let defaultStyleAction = UIPreviewAction(title: "Default Style", style: .Default) {action, controller in } let destructiveStyleAction = UIPreviewAction(title: "Destructive Style", style: .Destructive) {action, controller in } let selectedStyleAction = UIPreviewAction(title: "Selected Style", style: .Selected) {action, controller in } let actionGroup = UIPreviewActionGroup(title: "Group", style: .Default, actions: [defaultStyleAction, defaultStyleAction, defaultStyleAction]) return [defaultStyleAction, destructiveStyleAction, selectedStyleAction, actionGroup] } }
  • 43.
    Peek Quick Actions classPreviewedViewController: UIViewController { override func previewActionItems() -> [UIPreviewActionItem] { let defaultStyleAction = UIPreviewAction(title: "Default Style", style: .Default) {action, controller in } let destructiveStyleAction = UIPreviewAction(title: "Destructive Style", style: .Destructive) {action, controller in } let selectedStyleAction = UIPreviewAction(title: "Selected Style", style: .Selected) {action, controller in } return [defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction, defaultStyleAction, destructiveStyleAction, selectedStyleAction] } }
  • 44.
    Detecting 3D Touch publicclass UITraitCollection : NSObject, NSCopying, NSSecureCoding { // … public var forceTouchCapability: UIForceTouchCapability { get } // … } public enum UIForceTouchCapability : Int { case Unknown case Unavailable case Available }
  • 45.
    Detecting 3D Touch publicclass UITraitCollection : NSObject, NSCopying, NSSecureCoding { // … public var forceTouchCapability: UIForceTouchCapability { get } // … } public enum UIForceTouchCapability : Int { case Unknown case Unavailable case Available } /*! Trait environments expose a trait collection that describes their environment. */ public protocol UITraitEnvironment : NSObjectProtocol { public var traitCollection: UITraitCollection { get } /*! To be overridden as needed to provide custom behavior when the environment's traits change. */ public func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) }
  • 46.
    Detecting 3D Touch publicclass UITraitCollection : NSObject, NSCopying, NSSecureCoding { // … public var forceTouchCapability: UIForceTouchCapability { get } // … } public enum UIForceTouchCapability : Int { case Unknown case Unavailable case Available } /*! Trait environments expose a trait collection that describes their environment. */ public protocol UITraitEnvironment : NSObjectProtocol { public var traitCollection: UITraitCollection { get } /*! To be overridden as needed to provide custom behavior when the environment's traits change. */ public func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) } The following interface classes adopt this protocol: UIScreen, UIWindow, UIViewController, UIPresentationController, and UIView.
  • 47.
    Summary • UITouch.force • StaticQuick Actions • Dynamic Quick Actions • Peek and Pop • Peek Quick Actions • Detecting 3D Touch
  • 48.
    Summary • UITouch.force • StaticQuick Actions • Dynamic Quick Actions • Peek and Pop • Peek Quick Actions • Detecting 3D Touch
  • 49.
    Summary • UITouch.force • StaticQuick Actions • Dynamic Quick Actions • Peek and Pop • Peek Quick Actions • Detecting 3D Touch
  • 50.
    Summary • UITouch.force • StaticQuick Actions • Dynamic Quick Actions • Peek and Pop • Peek Quick Actions • Detecting 3D Touch
  • 51.
    Summary • UITouch.force • StaticQuick Actions • Dynamic Quick Actions • Peek and Pop • Peek Quick Actions • Detecting 3D Touch
  • 52.
    Summary • UITouch.force • StaticQuick Actions • Dynamic Quick Actions • Peek and Pop • Peek Quick Actions • Detecting 3D Touch
  • 53.
    Thank you forlistening