SlideShare a Scribd company logo
1 of 56
Download to read offline
Revenge of the ’80s

Cut/Copy/Paste, Undo/Redo, and
More Big Hits
Chris Adamson • @invalidname

CocoaConf Chicago, March 2015
Edit Menu
• Undo: Returns you to the state prior to your
previous action˝
• Redo: Undoes an undo˝
• Cut: Remove selection, put it on clipboard˝
• Copy: Put selection on clipboard˝
• Paste: Insert contents of clipboard˝
• Delete: Delete selection
Demo
DuranDuranFans
Where do these events
come from?
MainMenu.nib or
Main.storyboard
First Responder
The Responder Chain
“The responder chain is a linked
series of responder objects to which
an event or action message is
applied. When a given responder
object doesn’t handle a particular
message, the object passes the
message to its successor in the chain
(that is, its next responder). This
allows responder objects to delegate
responsibility for handling the
message to other, typically higher-
level objects.”
NSResponder
• NSView˝
• NSViewController˝
• NSWindow˝
• NSWindowController
Responding to menu
events
@IBAction func delete (sender: AnyObject!) {˝
NSLog ("delete")˝
deleteSelectedFan()˝
}
–Clueless User
“But I didn’t mean to delete!”
NSUndoManager
• Object to manage a stack of undoable
actions˝
• You get an undoManager property for
free from NSResponder and NSDocument˝
• Test with canUndo/canRedo, perform
with undo and redo
Undoable Actions
• UndoManager.registerUndoWithTarget(_:
selector:object:) adds an undoable action
to the stack˝
• Target is the object to be called˝
• Selector is a method that takes one
parameter˝
• Object is the parameter
Deleting a Fan
func deleteSelectedFan() {˝
let index = fansTable.selectedRow˝
if index < 0 {˝
return˝
}˝
let deletedFan = fans[index]˝
let undoInfo = ["fan" : deletedFan , "index" : index]˝
undoManager?.registerUndoWithTarget(self,˝
selector: “undoDeleteFan:",˝
object: undoInfo)˝
deleteAtIndex(index)˝
}
undoDeleteFan
func undoDeleteFan (userInfo: AnyObject?) {˝
NSLog ("undoDeleteFan")˝
if let undoInfo = userInfo as? [String : AnyObject] {˝
let fan = undoInfo["fan"] as Fan?˝
let index = undoInfo["index"] as Int?˝
if fan != nil && index != nil {˝
addFan (fan!, atIndex: index!)˝
}˝
}˝
}
addFan
func addFan (fan: Fan, atIndex: Int) {˝
NSLog ("addFan")˝
fans.insert(fan, atIndex: atIndex)˝
let undoInfo = ["fan" : fan, "index" : atIndex]˝
undoManager?.registerUndoWithTarget(self,˝
selector: “undoAddFan:",˝
object: undoInfo)˝
fansTable.reloadData()˝
}
Redo
• Redoable actions are simply undoable
actions registered in the course of
performing an undo˝
• undoDeleteFan() calls addFan(), which
registers an undoable action to perform a
delete (undoAddFan(), which calls
deleteAtIndex())˝
• So, redo calls the delete
Undo considerations
• Undo action has to contain all the data
needed to restore old state˝
• For a delete, this means the deleted object is
typically retained by the undo manager˝
• Memory considerations˝
• Performance considerations˝
• Consider setting levelsOfUndo, or clear stack
with removeAllActions
Cut / Copy / Paste
NSPasteboard
• Storage for cut/copy/paste items˝
• System provides a “general” pasteboard,
plus named pasteboards for drag-and-
drop, fonts, and rulers˝
• Can also create your own pasteboard
One object, many
flavors
• Each app has its own distinct abilities and
limitations when it comes to reading and
writing data˝
• So, objects on the pasteboard can have many
different representations˝
• Image could PDF, PNG, JPEG, etc.; text could
be plain-text, RTF, etc.˝
• Cut/copy provide multiple representations;
paste indicates the types it can work with
Uniform Type
Identifiers (UTI)
UTIs
• A UTI is a string that describes a type of
data˝
• Arranged in a hierarchy: more specific
types “conform to” a more general type˝
• public.jpeg conforms to public.image
Standard UTIs
• Defined in CoreServices.h˝
• Text types: kUTTypePlainText,
kUTTypeHTML, kUTTypeCSource˝
• Image types: kUTTypeJPEG, kUTTypePNG˝
• Others: kUTTypePDF, kUTTypeMPEG4,
kUTTypeVCard, etc.
Custom UTIs
• Create a unique reverse-DNS string
(“com.cocoaconf.mytype”) and declare it
in your app’s Info.plist
Writing to
NSPasteboard
• NSPasteboard.setData(forType:) takes an
NSData and a string type˝
• Type is either a UTI or some legacy
NSPasteboard constants˝
• NSPasteboard.writeObjects() lets you
write multiple objects
NSPasteboardWriting
• Protocol for classes you want to add to
the pasteboard with writeObjects()˝
• writeableTypesForPasteboard(): Indicates
the types you provide˝
• pasteboardPropertyListForType():
Provides the contents for a given type,
typically as an NSData
Fan class
let FAN_UTI = "com.subfurther.cocoaconf.80s.fan"˝
class Fan : NSObject, NSCoding, NSPasteboardWriting,˝
NSPasteboardReading {˝
var firstName: String?˝
var lastName: String?˝
var favoriteSong: String?
Fan class:
NSPasteboardWriting
func writableTypesForPasteboard(pasteboard: NSPasteboard!) -> [AnyObject]! {˝
return [FAN_UTI, NSPasteboardTypeString]˝
}˝
˝
func pasteboardPropertyListForType(type: String!) -> AnyObject! {˝
switch type {˝
case NSPasteboardTypeString:˝
return fullName()˝
case FAN_UTI:˝
let data = NSMutableData()˝
let archiver = NSKeyedArchiver(forWritingWithMutableData: data)˝
archiver.encodeObject(firstName, forKey: "firstName")˝
archiver.encodeObject(lastName, forKey: "lastName")˝
archiver.encodeObject(favoriteSong, forKey: "favoriteSong")˝
archiver.finishEncoding()˝
return data˝
default:˝
return nil˝
}˝
}
Reading from
NSPasteboard
• Fetch contents for a given type with
dataForType(), stringForType, or
propertyListForType()˝
• Initialize custom objects from
pasteboard data by implementing
NSPasteboardReading
NSPasteboardReading
• readableTypesForPasteboard(): Indicates
which types you read, in order of
preference.˝
• Initialize from pasteboard:˝
• Obj-C:
initWithPasteboardPropertyList:ofType˝
• Swift: init
(pasteboardPropertyList:ofType:)
Fan class:
NSPasteboardReading
class func readableTypesForPasteboard(pasteboard: NSPasteboard!) -> [AnyObject]! {˝
return [FAN_UTI]˝
}˝
˝
required init?(pasteboardPropertyList propertyList: AnyObject!, ofType type: String!) {˝
super.init()˝
switch type {˝
case NSPasteboardTypeString:˝
return nil˝
case FAN_UTI:˝
if let data = propertyList as? NSData {˝
let unarchiver = NSKeyedUnarchiver(forReadingWithData: data)˝
self.firstName = unarchiver.decodeObjectForKey("firstName") as String?˝
self.lastName = unarchiver.decodeObjectForKey("lastName") as String?˝
self.favoriteSong = unarchiver.decodeObjectForKey("favoriteSong") as String?˝
}˝
default:˝
return nil // die˝
}˝
}
What about mobile?
Demo
HammerFans
HammerFans
• Functionally equivalent to DuranDuranFans
• Mostly reused the Fan class
• Mostly reused the undo/redo code
• undoManager provided by UIResponder & UIDocument
• Pasteboard is pretty different
• But where do the events come from, since there’s no MainMenu?
UIMenuController
• Commonly shown in response to a long-press gesture
• Shows a popup menu targeted at a given CGRect in a UIView
• Allows you to add UIMenuItems
• Your own items will always appear after all of Apple’s,
frequently on a second or third “page” of menu items
Show UIMenuController
// MARK - UIMenuController
@IBAction func handleTableLongPress(sender: AnyObject) {
var targetRect = CGRect (x: self.view.bounds.width/2.0,
y: self.view.bounds.height/2.0,
width: 0, height: 0)
if let recognizer = sender as? UIGestureRecognizer {
let touchPoint = recognizer.locationInView(fansTable)
targetRect = CGRect(origin: touchPoint, size: CGSizeZero)
// also auto-select row, if possible
let indexPath = fansTable.indexPathForRowAtPoint (touchPoint)
if indexPath != nil {
fansTable.selectRowAtIndexPath(indexPath!, animated: false,
scrollPosition: .None)
}
}
UIMenuController.sharedMenuController().setTargetRect(targetRect,
inView: fansTable)
UIMenuController.sharedMenuController().setMenuVisible(true,
animated: true)
}
UIResponderStandardEditActions
• Informal protocol on NSObject
• Defines actions that editing controllers are anticipated to
provide:
• cut:, copy:, delete:, paste:, select:, selectAll:
• By default, shake gesture will send undo: or redo: up the
responder chain too
Handling a menu action
• Your view or viewController must override
UIResponder.canBecomeFirstResponder() to return true/YES
• When queried with UIResponder.canPerformAction(withSender:),
inspect the selector to see if it’s a method you want to handle.
• If so, return true/YES; if not, typically call
super.canPerformAction:
• For fun, return true for everything — you’ll see all the
accessibility, I18N, and spelling/autocomplete calls. Beware: any
action starting with “_” is a private API
UIResponder.canPerformAction()
override func canPerformAction(action: Selector,
withSender sender: AnyObject?) -> Bool {
switch action {
case "copy:","cut:","paste:","delete:":
return true
default:
return super.canPerformAction(action, withSender: sender)
}
}
Responding to menu events
override func delete (sender: AnyObject!) {
NSLog ("delete")
deleteSelectedFan()
}
This is identical to the Mac version, except that delete: must be
marked as an override in Swift (overrides
UIResponderStandardEditActions)
UIPasteboard
• Similar in spirit to NSPasteboard
• Two system pasteboards, general and name-find, plus you can
make your own
• Get and set pasteboard data by a string type, typically a UTI
• Does not provide equivalents for NSPasteboardReading,
NSPasteboardWriting
• Common UTIs defined in MobileCoreServices.h
Custom objects on pasteboard
• Get/set the UIPasteboard.items property
• Array of dictionaries
• Each dictionary is keyed by its type, and the value is the
representation (often NSData) of that type
Writing to UIPasteboard
func writeSelectedFanToPasteboard () {
let selection = selectedFanAndIndex()
if selection != nil {
UIPasteboard.generalPasteboard().items =
[selection!.fan.dictionaryForPasteboard()]
}
}
func dictionaryForPasteboard() -> [NSString : AnyObject] {
var dictionary : [NSString : AnyObject] = [:]
// public.text
dictionary [kUTTypeText] = self.fullName()
// FAN_UTI
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWithMutableData: data)
archiver.encodeObject(firstName, forKey: "firstName")
archiver.encodeObject(lastName, forKey: "lastName")
archiver.encodeObject(favoriteSong, forKey: "favoriteSong")
archiver.finishEncoding()
dictionary [FAN_UTI] = data
return dictionary
}
ViewController
Fan
Reading from UIPasteboard
override func paste (sender: AnyObject!) {
NSLog ("paste")
let pasteboard = UIPasteboard.generalPasteboard()
if let pasteData = pasteboard.dataForPasteboardType(FAN_UTI) {
if let fan = Fan (pasteboardData: pasteData) {
addFan(fan, atIndex: fans.count)
}
}
}
init? (pasteboardData: NSData) {
super.init()
let unarchiver = NSKeyedUnarchiver(forReadingWithData: pasteboardData)
self.firstName = unarchiver.decodeObjectForKey("firstName") as String?
self.lastName = unarchiver.decodeObjectForKey("lastName") as String?
self.favoriteSong = unarchiver.decodeObjectForKey("favoriteSong") as String?
if self.firstName == nil || self.lastName == nil {
return nil
}
}
ViewController
Fan
Wrap-up
Wrap-up: Undo/Redo
• Undo Manager is usually provided to you
through window/view controller or
document class˝
• You register your undoable actions with a
target/action metaphor. Your action must
be able to re-create the pre-edit state.˝
• Redos are just undoable actions
registered while performing an undo
Wrap-Up: Cut, Copy,
Paste
• UTIs define how data is represented on
the pasteboard (also in documents, but
that’s another session)˝
• Try to provide a range of UTIs so other
apps can get a richer copy of your data˝
• You can define your own UTIs in the app
target’s Info.plist (on iOS too!)
Q&A
@invalidname — invalidname@gmail.com˝
Slides: http://slideshare.com/invalidname˝
Code: http://github.com/invalidname

More Related Content

What's hot

Stored Procedures and MUMPS for DivConq
 Stored Procedures and  MUMPS for DivConq  Stored Procedures and  MUMPS for DivConq
Stored Procedures and MUMPS for DivConq eTimeline, LLC
 
JQuery New Evolution
JQuery New EvolutionJQuery New Evolution
JQuery New EvolutionAllan Huang
 
Workshop Sul Refactoring Agile Day 2008
Workshop Sul Refactoring   Agile Day 2008Workshop Sul Refactoring   Agile Day 2008
Workshop Sul Refactoring Agile Day 2008Tommaso Torti
 
Benchy, python framework for performance benchmarking of Python Scripts
Benchy, python framework for performance benchmarking  of Python ScriptsBenchy, python framework for performance benchmarking  of Python Scripts
Benchy, python framework for performance benchmarking of Python ScriptsMarcel Caraciolo
 
Clean code with google guava jee conf
Clean code with google guava jee confClean code with google guava jee conf
Clean code with google guava jee confIgor Anishchenko
 
Google guava overview
Google guava overviewGoogle guava overview
Google guava overviewSteve Min
 
Hello Swift Final 5/5 - Structures and Classes
Hello Swift Final 5/5 - Structures and ClassesHello Swift Final 5/5 - Structures and Classes
Hello Swift Final 5/5 - Structures and ClassesCody Yun
 
Jquery 13 cheatsheet_v1
Jquery 13 cheatsheet_v1Jquery 13 cheatsheet_v1
Jquery 13 cheatsheet_v1ilesh raval
 
Jquery 1.3 cheatsheet_v1
Jquery 1.3 cheatsheet_v1Jquery 1.3 cheatsheet_v1
Jquery 1.3 cheatsheet_v1Sultan Khan
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門Tsuyoshi Yamamoto
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Exercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera CymbronExercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera Cymbroncymbron
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
enchant js workshop on Calpoly
enchant js workshop  on Calpolyenchant js workshop  on Calpoly
enchant js workshop on CalpolyRyo Shimizu
 
A evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no androidA evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no androidRodrigo de Souza Castro
 
A clean(er) architecture
A clean(er) architectureA clean(er) architecture
A clean(er) architectureAndreaMaglie
 

What's hot (20)

Dmxedit
DmxeditDmxedit
Dmxedit
 
Stored Procedures and MUMPS for DivConq
 Stored Procedures and  MUMPS for DivConq  Stored Procedures and  MUMPS for DivConq
Stored Procedures and MUMPS for DivConq
 
JQuery New Evolution
JQuery New EvolutionJQuery New Evolution
JQuery New Evolution
 
Google Guava
Google GuavaGoogle Guava
Google Guava
 
Workshop Sul Refactoring Agile Day 2008
Workshop Sul Refactoring   Agile Day 2008Workshop Sul Refactoring   Agile Day 2008
Workshop Sul Refactoring Agile Day 2008
 
Benchy, python framework for performance benchmarking of Python Scripts
Benchy, python framework for performance benchmarking  of Python ScriptsBenchy, python framework for performance benchmarking  of Python Scripts
Benchy, python framework for performance benchmarking of Python Scripts
 
Clean code with google guava jee conf
Clean code with google guava jee confClean code with google guava jee conf
Clean code with google guava jee conf
 
Google guava overview
Google guava overviewGoogle guava overview
Google guava overview
 
Hello Swift Final 5/5 - Structures and Classes
Hello Swift Final 5/5 - Structures and ClassesHello Swift Final 5/5 - Structures and Classes
Hello Swift Final 5/5 - Structures and Classes
 
Understanding redux
Understanding reduxUnderstanding redux
Understanding redux
 
Jquery 13 cheatsheet_v1
Jquery 13 cheatsheet_v1Jquery 13 cheatsheet_v1
Jquery 13 cheatsheet_v1
 
Jquery 1.3 cheatsheet_v1
Jquery 1.3 cheatsheet_v1Jquery 1.3 cheatsheet_v1
Jquery 1.3 cheatsheet_v1
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Exercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera CymbronExercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera Cymbron
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Django
Django Django
Django
 
enchant js workshop on Calpoly
enchant js workshop  on Calpolyenchant js workshop  on Calpoly
enchant js workshop on Calpoly
 
A evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no androidA evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no android
 
A clean(er) architecture
A clean(er) architectureA clean(er) architecture
A clean(er) architecture
 

Viewers also liked

IOS Swift Language 3rd tutorial
IOS Swift Language 3rd tutorialIOS Swift Language 3rd tutorial
IOS Swift Language 3rd tutorialHassan A-j
 
Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)Chris Adamson
 
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)Chris Adamson
 
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...Chris Adamson
 
Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)Chris Adamson
 
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)Chris Adamson
 
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)Chris Adamson
 
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)Chris Adamson
 
Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014Chris Adamson
 
Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)Chris Adamson
 
Introduction to the Roku SDK
Introduction to the Roku SDKIntroduction to the Roku SDK
Introduction to the Roku SDKChris Adamson
 
Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)Chris Adamson
 
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)Chris Adamson
 
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Chris Adamson
 
Stupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las VegasStupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las VegasChris Adamson
 
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is FineForward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is FineChris Adamson
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to SwiftGiordano Scalzo
 
Swift Programming Language
Swift Programming LanguageSwift Programming Language
Swift Programming LanguageGiuseppe Arici
 

Viewers also liked (19)

IOS Swift Language 3rd tutorial
IOS Swift Language 3rd tutorialIOS Swift Language 3rd tutorial
IOS Swift Language 3rd tutorial
 
Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)
 
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
 
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
 
Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)
 
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
 
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
 
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
 
Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014
 
Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)
 
Introduction to the Roku SDK
Introduction to the Roku SDKIntroduction to the Roku SDK
Introduction to the Roku SDK
 
Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)
 
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
 
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
 
Stupid Video Tricks
Stupid Video TricksStupid Video Tricks
Stupid Video Tricks
 
Stupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las VegasStupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las Vegas
 
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is FineForward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
 
Swift Programming Language
Swift Programming LanguageSwift Programming Language
Swift Programming Language
 

Similar to Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf Chicago, 2015)

How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF Luc Bors
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSAdam L Barrett
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)Rick Copeland
 
Academy PRO: React native - navigation
Academy PRO: React native - navigationAcademy PRO: React native - navigation
Academy PRO: React native - navigationBinary Studio
 
Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Ben Lesh
 
Managing Complex UI using xState
Managing Complex UI using xStateManaging Complex UI using xState
Managing Complex UI using xStateXavier Lozinguez
 
Aplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackAplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackNelson Glauber Leal
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05cKaz Yoshikawa
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KThomas Fuchs
 
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your DataMongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your DataMongoDB
 
Student information system
Student information systemStudent information system
Student information systempatrick7772
 
How fast ist it really? Benchmarking in practice
How fast ist it really? Benchmarking in practiceHow fast ist it really? Benchmarking in practice
How fast ist it really? Benchmarking in practiceTobias Pfeiffer
 

Similar to Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf Chicago, 2015) (20)

How te bring common UI patterns to ADF
How te bring common UI patterns to ADFHow te bring common UI patterns to ADF
How te bring common UI patterns to ADF
 
jQuery
jQueryjQuery
jQuery
 
How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJS
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
ReactJS
ReactJSReactJS
ReactJS
 
Day 2
Day 2Day 2
Day 2
 
Android 3
Android 3Android 3
Android 3
 
Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)
 
Academy PRO: React native - navigation
Academy PRO: React native - navigationAcademy PRO: React native - navigation
Academy PRO: React native - navigation
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
 
Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016
 
Managing Complex UI using xState
Managing Complex UI using xStateManaging Complex UI using xState
Managing Complex UI using xState
 
Aplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackAplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & Jetpack
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05c
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your DataMongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
 
Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
 
Student information system
Student information systemStudent information system
Student information system
 
How fast ist it really? Benchmarking in practice
How fast ist it really? Benchmarking in practiceHow fast ist it really? Benchmarking in practice
How fast ist it really? Benchmarking in practice
 

More from Chris Adamson

Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)Chris Adamson
 
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)Chris Adamson
 
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)Chris Adamson
 
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...Chris Adamson
 
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineCocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineChris Adamson
 
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)Chris Adamson
 
iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)Chris Adamson
 
Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
Core Audio in iOS 6 (CocoaConf San Jose, April 2013) Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
Core Audio in iOS 6 (CocoaConf San Jose, April 2013) Chris Adamson
 
Core Audio in iOS 6 (CocoaConf DC, March 2013)
Core Audio in iOS 6 (CocoaConf DC, March 2013)Core Audio in iOS 6 (CocoaConf DC, March 2013)
Core Audio in iOS 6 (CocoaConf DC, March 2013)Chris Adamson
 
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)Chris Adamson
 
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)Chris Adamson
 
Core Audio Intro (Detroit Mobile City 2013)
Core Audio Intro (Detroit Mobile City 2013)Core Audio Intro (Detroit Mobile City 2013)
Core Audio Intro (Detroit Mobile City 2013)Chris Adamson
 
Objective-C Is Not Java
Objective-C Is Not JavaObjective-C Is Not Java
Objective-C Is Not JavaChris Adamson
 
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)Chris Adamson
 

More from Chris Adamson (14)

Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
 
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
 
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
 
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
 
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineCocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
 
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
 
iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)
 
Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
Core Audio in iOS 6 (CocoaConf San Jose, April 2013) Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
 
Core Audio in iOS 6 (CocoaConf DC, March 2013)
Core Audio in iOS 6 (CocoaConf DC, March 2013)Core Audio in iOS 6 (CocoaConf DC, March 2013)
Core Audio in iOS 6 (CocoaConf DC, March 2013)
 
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
 
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
 
Core Audio Intro (Detroit Mobile City 2013)
Core Audio Intro (Detroit Mobile City 2013)Core Audio Intro (Detroit Mobile City 2013)
Core Audio Intro (Detroit Mobile City 2013)
 
Objective-C Is Not Java
Objective-C Is Not JavaObjective-C Is Not Java
Objective-C Is Not Java
 
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)
 

Recently uploaded

Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 

Recently uploaded (20)

Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 

Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf Chicago, 2015)

  • 1. Revenge of the ’80s
 Cut/Copy/Paste, Undo/Redo, and More Big Hits Chris Adamson • @invalidname
 CocoaConf Chicago, March 2015
  • 2.
  • 3.
  • 4.
  • 5. Edit Menu • Undo: Returns you to the state prior to your previous action˝ • Redo: Undoes an undo˝ • Cut: Remove selection, put it on clipboard˝ • Copy: Put selection on clipboard˝ • Paste: Insert contents of clipboard˝ • Delete: Delete selection
  • 8. Where do these events come from?
  • 11. The Responder Chain “The responder chain is a linked series of responder objects to which an event or action message is applied. When a given responder object doesn’t handle a particular message, the object passes the message to its successor in the chain (that is, its next responder). This allows responder objects to delegate responsibility for handling the message to other, typically higher- level objects.”
  • 12. NSResponder • NSView˝ • NSViewController˝ • NSWindow˝ • NSWindowController
  • 13. Responding to menu events @IBAction func delete (sender: AnyObject!) {˝ NSLog ("delete")˝ deleteSelectedFan()˝ }
  • 14. –Clueless User “But I didn’t mean to delete!”
  • 15. NSUndoManager • Object to manage a stack of undoable actions˝ • You get an undoManager property for free from NSResponder and NSDocument˝ • Test with canUndo/canRedo, perform with undo and redo
  • 16. Undoable Actions • UndoManager.registerUndoWithTarget(_: selector:object:) adds an undoable action to the stack˝ • Target is the object to be called˝ • Selector is a method that takes one parameter˝ • Object is the parameter
  • 17. Deleting a Fan func deleteSelectedFan() {˝ let index = fansTable.selectedRow˝ if index < 0 {˝ return˝ }˝ let deletedFan = fans[index]˝ let undoInfo = ["fan" : deletedFan , "index" : index]˝ undoManager?.registerUndoWithTarget(self,˝ selector: “undoDeleteFan:",˝ object: undoInfo)˝ deleteAtIndex(index)˝ }
  • 18. undoDeleteFan func undoDeleteFan (userInfo: AnyObject?) {˝ NSLog ("undoDeleteFan")˝ if let undoInfo = userInfo as? [String : AnyObject] {˝ let fan = undoInfo["fan"] as Fan?˝ let index = undoInfo["index"] as Int?˝ if fan != nil && index != nil {˝ addFan (fan!, atIndex: index!)˝ }˝ }˝ }
  • 19. addFan func addFan (fan: Fan, atIndex: Int) {˝ NSLog ("addFan")˝ fans.insert(fan, atIndex: atIndex)˝ let undoInfo = ["fan" : fan, "index" : atIndex]˝ undoManager?.registerUndoWithTarget(self,˝ selector: “undoAddFan:",˝ object: undoInfo)˝ fansTable.reloadData()˝ }
  • 20. Redo • Redoable actions are simply undoable actions registered in the course of performing an undo˝ • undoDeleteFan() calls addFan(), which registers an undoable action to perform a delete (undoAddFan(), which calls deleteAtIndex())˝ • So, redo calls the delete
  • 21. Undo considerations • Undo action has to contain all the data needed to restore old state˝ • For a delete, this means the deleted object is typically retained by the undo manager˝ • Memory considerations˝ • Performance considerations˝ • Consider setting levelsOfUndo, or clear stack with removeAllActions
  • 22. Cut / Copy / Paste
  • 23. NSPasteboard • Storage for cut/copy/paste items˝ • System provides a “general” pasteboard, plus named pasteboards for drag-and- drop, fonts, and rulers˝ • Can also create your own pasteboard
  • 24. One object, many flavors • Each app has its own distinct abilities and limitations when it comes to reading and writing data˝ • So, objects on the pasteboard can have many different representations˝ • Image could PDF, PNG, JPEG, etc.; text could be plain-text, RTF, etc.˝ • Cut/copy provide multiple representations; paste indicates the types it can work with
  • 26. UTIs • A UTI is a string that describes a type of data˝ • Arranged in a hierarchy: more specific types “conform to” a more general type˝ • public.jpeg conforms to public.image
  • 27. Standard UTIs • Defined in CoreServices.h˝ • Text types: kUTTypePlainText, kUTTypeHTML, kUTTypeCSource˝ • Image types: kUTTypeJPEG, kUTTypePNG˝ • Others: kUTTypePDF, kUTTypeMPEG4, kUTTypeVCard, etc.
  • 28. Custom UTIs • Create a unique reverse-DNS string (“com.cocoaconf.mytype”) and declare it in your app’s Info.plist
  • 29. Writing to NSPasteboard • NSPasteboard.setData(forType:) takes an NSData and a string type˝ • Type is either a UTI or some legacy NSPasteboard constants˝ • NSPasteboard.writeObjects() lets you write multiple objects
  • 30. NSPasteboardWriting • Protocol for classes you want to add to the pasteboard with writeObjects()˝ • writeableTypesForPasteboard(): Indicates the types you provide˝ • pasteboardPropertyListForType(): Provides the contents for a given type, typically as an NSData
  • 31. Fan class let FAN_UTI = "com.subfurther.cocoaconf.80s.fan"˝ class Fan : NSObject, NSCoding, NSPasteboardWriting,˝ NSPasteboardReading {˝ var firstName: String?˝ var lastName: String?˝ var favoriteSong: String?
  • 32. Fan class: NSPasteboardWriting func writableTypesForPasteboard(pasteboard: NSPasteboard!) -> [AnyObject]! {˝ return [FAN_UTI, NSPasteboardTypeString]˝ }˝ ˝ func pasteboardPropertyListForType(type: String!) -> AnyObject! {˝ switch type {˝ case NSPasteboardTypeString:˝ return fullName()˝ case FAN_UTI:˝ let data = NSMutableData()˝ let archiver = NSKeyedArchiver(forWritingWithMutableData: data)˝ archiver.encodeObject(firstName, forKey: "firstName")˝ archiver.encodeObject(lastName, forKey: "lastName")˝ archiver.encodeObject(favoriteSong, forKey: "favoriteSong")˝ archiver.finishEncoding()˝ return data˝ default:˝ return nil˝ }˝ }
  • 33. Reading from NSPasteboard • Fetch contents for a given type with dataForType(), stringForType, or propertyListForType()˝ • Initialize custom objects from pasteboard data by implementing NSPasteboardReading
  • 34. NSPasteboardReading • readableTypesForPasteboard(): Indicates which types you read, in order of preference.˝ • Initialize from pasteboard:˝ • Obj-C: initWithPasteboardPropertyList:ofType˝ • Swift: init (pasteboardPropertyList:ofType:)
  • 35. Fan class: NSPasteboardReading class func readableTypesForPasteboard(pasteboard: NSPasteboard!) -> [AnyObject]! {˝ return [FAN_UTI]˝ }˝ ˝ required init?(pasteboardPropertyList propertyList: AnyObject!, ofType type: String!) {˝ super.init()˝ switch type {˝ case NSPasteboardTypeString:˝ return nil˝ case FAN_UTI:˝ if let data = propertyList as? NSData {˝ let unarchiver = NSKeyedUnarchiver(forReadingWithData: data)˝ self.firstName = unarchiver.decodeObjectForKey("firstName") as String?˝ self.lastName = unarchiver.decodeObjectForKey("lastName") as String?˝ self.favoriteSong = unarchiver.decodeObjectForKey("favoriteSong") as String?˝ }˝ default:˝ return nil // die˝ }˝ }
  • 37.
  • 38.
  • 39.
  • 40. Demo
  • 42. HammerFans • Functionally equivalent to DuranDuranFans • Mostly reused the Fan class • Mostly reused the undo/redo code • undoManager provided by UIResponder & UIDocument • Pasteboard is pretty different • But where do the events come from, since there’s no MainMenu?
  • 43. UIMenuController • Commonly shown in response to a long-press gesture • Shows a popup menu targeted at a given CGRect in a UIView • Allows you to add UIMenuItems • Your own items will always appear after all of Apple’s, frequently on a second or third “page” of menu items
  • 44. Show UIMenuController // MARK - UIMenuController @IBAction func handleTableLongPress(sender: AnyObject) { var targetRect = CGRect (x: self.view.bounds.width/2.0, y: self.view.bounds.height/2.0, width: 0, height: 0) if let recognizer = sender as? UIGestureRecognizer { let touchPoint = recognizer.locationInView(fansTable) targetRect = CGRect(origin: touchPoint, size: CGSizeZero) // also auto-select row, if possible let indexPath = fansTable.indexPathForRowAtPoint (touchPoint) if indexPath != nil { fansTable.selectRowAtIndexPath(indexPath!, animated: false, scrollPosition: .None) } } UIMenuController.sharedMenuController().setTargetRect(targetRect, inView: fansTable) UIMenuController.sharedMenuController().setMenuVisible(true, animated: true) }
  • 45. UIResponderStandardEditActions • Informal protocol on NSObject • Defines actions that editing controllers are anticipated to provide: • cut:, copy:, delete:, paste:, select:, selectAll: • By default, shake gesture will send undo: or redo: up the responder chain too
  • 46. Handling a menu action • Your view or viewController must override UIResponder.canBecomeFirstResponder() to return true/YES • When queried with UIResponder.canPerformAction(withSender:), inspect the selector to see if it’s a method you want to handle. • If so, return true/YES; if not, typically call super.canPerformAction: • For fun, return true for everything — you’ll see all the accessibility, I18N, and spelling/autocomplete calls. Beware: any action starting with “_” is a private API
  • 47. UIResponder.canPerformAction() override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { switch action { case "copy:","cut:","paste:","delete:": return true default: return super.canPerformAction(action, withSender: sender) } }
  • 48. Responding to menu events override func delete (sender: AnyObject!) { NSLog ("delete") deleteSelectedFan() } This is identical to the Mac version, except that delete: must be marked as an override in Swift (overrides UIResponderStandardEditActions)
  • 49. UIPasteboard • Similar in spirit to NSPasteboard • Two system pasteboards, general and name-find, plus you can make your own • Get and set pasteboard data by a string type, typically a UTI • Does not provide equivalents for NSPasteboardReading, NSPasteboardWriting • Common UTIs defined in MobileCoreServices.h
  • 50. Custom objects on pasteboard • Get/set the UIPasteboard.items property • Array of dictionaries • Each dictionary is keyed by its type, and the value is the representation (often NSData) of that type
  • 51. Writing to UIPasteboard func writeSelectedFanToPasteboard () { let selection = selectedFanAndIndex() if selection != nil { UIPasteboard.generalPasteboard().items = [selection!.fan.dictionaryForPasteboard()] } } func dictionaryForPasteboard() -> [NSString : AnyObject] { var dictionary : [NSString : AnyObject] = [:] // public.text dictionary [kUTTypeText] = self.fullName() // FAN_UTI let data = NSMutableData() let archiver = NSKeyedArchiver(forWritingWithMutableData: data) archiver.encodeObject(firstName, forKey: "firstName") archiver.encodeObject(lastName, forKey: "lastName") archiver.encodeObject(favoriteSong, forKey: "favoriteSong") archiver.finishEncoding() dictionary [FAN_UTI] = data return dictionary } ViewController Fan
  • 52. Reading from UIPasteboard override func paste (sender: AnyObject!) { NSLog ("paste") let pasteboard = UIPasteboard.generalPasteboard() if let pasteData = pasteboard.dataForPasteboardType(FAN_UTI) { if let fan = Fan (pasteboardData: pasteData) { addFan(fan, atIndex: fans.count) } } } init? (pasteboardData: NSData) { super.init() let unarchiver = NSKeyedUnarchiver(forReadingWithData: pasteboardData) self.firstName = unarchiver.decodeObjectForKey("firstName") as String? self.lastName = unarchiver.decodeObjectForKey("lastName") as String? self.favoriteSong = unarchiver.decodeObjectForKey("favoriteSong") as String? if self.firstName == nil || self.lastName == nil { return nil } } ViewController Fan
  • 54. Wrap-up: Undo/Redo • Undo Manager is usually provided to you through window/view controller or document class˝ • You register your undoable actions with a target/action metaphor. Your action must be able to re-create the pre-edit state.˝ • Redos are just undoable actions registered while performing an undo
  • 55. Wrap-Up: Cut, Copy, Paste • UTIs define how data is represented on the pasteboard (also in documents, but that’s another session)˝ • Try to provide a range of UTIs so other apps can get a richer copy of your data˝ • You can define your own UTIs in the app target’s Info.plist (on iOS too!)
  • 56. Q&A @invalidname — invalidname@gmail.com˝ Slides: http://slideshare.com/invalidname˝ Code: http://github.com/invalidname