Active Filters: UIKit

Topics

#45: Pasteboards πŸ“‹

Topics

There's a ton of great functionality packed in the UIPasteboard and NSPasteboard classes. Let's take a look at some things you can do:

Get the System Pasteboard

// iOS

let pb = UIPasteboard.generalPasteboard()

// OS X

let pb = NSPasteboard.generalPasteboard()

Copy to Clipboard

On iOS, similar convenience methods exist for images, URLs, and colors.

// iOS

pb.string = "Delicious!"

// OS X

pb.clearContents()
pb.setString("Delicious!", forType: NSPasteboardTypeString)

Read Contents

On OS X, you'll use the NSPasteboardType* family of keys.

pb.string // iOS

pb.stringForType(NSPasteboardTypeString) // OS X

Get Notified Of Changes

// iOS only. On OS X, you'll need to poll :(

NSNotificationCenter.defaultCenter().addObserver(   self,   selector: "pasteboardChanged:",
  name: UIPasteboardChangedNotification,
  object: nil
)

func pasteboardChanged(n: NSNotification) {
  if let pb = n.object as? UIPasteboard {
    print(pb.items)
  }
}

Share Data Between Apps

If you're using App Groups, you can share data between your apps by creating your own custom keyboard:

let customPB = UIPasteboard(
  name: "com.littlebites.spaceships",
  create: true
)

customPB!.persistent = true

Copy Animated GIF to Clipboard

if let GIFData = NSData(contentsOfURL: GIFURL) {
  pb.setData(
    GIFData,
      forPasteboardType: "com.compuserve.gif"
  )
}

Topics

#44: UIAlertController ⚠️

Topics

Alert Views and Action Sheets are some of the oldest parts of UIKit. In iOS 8, Apple overhauled how you create and display them using UIAlertController. Let's take a look at how it can improve the adaptability of your UI as well as the readability of your code:

@IBAction func launchButtonTapped(launchButton: UIButton) {
  let controller: UIAlertController = UIAlertController(
    title: "Where To?",
    message: "Where would you like to go?",
    preferredStyle: .ActionSheet
  )

  controller.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))

  controller.addAction(UIAlertAction(title: "Home", style: .Default) { action in
    flyToPlace("Home")
  })

  controller.addAction(UIAlertAction(title: "Pluto", style: .Default) { action in
    flyToPlace("Pluto")
  })

  controller.popoverPresentationController?.sourceView = launchButton

  presentViewController(controller, animated: true, completion: nil)
}

First we create the controller, and set the style to .ActionSheet. We could have used .Alert instead, and our actions would be presented as an Alert View.

Then we add actions, each with a block callback that will fire when the action is selected. A nil handler will have no action.

Then we set .sourceView to the button that originated the action so our action sheet's arrow will β€˜point' to the right spot on iPad.

And finally we present our controller just like any other view controller.

Topics

#43: Local Notifications πŸ“

Topics

Local Notifications are a way to display a system notification to a user of your app without needing to implement a push server. You simply schedule them using an NSDate and they are presented at that time. Let's take a look.

Register for Notifications

Since iOS 8, we've had two different things to register for: User Notifications and Remote notifications. Since we'll be β€œsending” Local Notifications, we only need to register for User Notifications.

In didFinishLaunchingWithOptions:

application.registerUserNotificationSettings(
  UIUserNotificationSettings(
    forTypes: [.Alert, .Sound, .Badge], 
    categories: nil
  )
)

This will prompt the user for permission to see notifications, except instead of coming from a backend server, they'll be generated locally on the device.

Schedule a Local Notification

After we get the didRegisterUserNotificationSettings callback, we're ready to schedule a notification. We use the UILocalNotification class to configure all the aspects of how the notification will look and behave:

func landSpaceship() {
  autopilotPerformAction(.LandShip)

  let landingTime = 30.0 // in seconds

  let n = UILocalNotification()

  n.alertTitle = "Spaceship Landed!"
  n.alertBody = "Good news everyone! Our ship has successfully landed."

  n.timeZone = NSCalendar.currentCalendar().timeZone
  n.fireDate = NSDate(timeIntervalSinceNow: landingTime)

  UIApplication.sharedApplication().scheduleLocalNotification(n)
}

Topics

#38: Gesture Recognizer Basics πŸ‘†

Topics

Gesture Recognizers are an powerful way to build interactions into your app. Let's look at the basics with a couple of example use cases:

Two Finger Long Press

Here we'll build a gesture that fires when the user touches the screen with two fingers for 1 second.

func handleLongPressGesture(gr: UILongPressGestureRecognizer) {
  sendHeartbeat()
}

let longPressGR = UILongPressGestureRecognizer(
  target: self, 
  action: "handleLongPressGesture:"
)

// we allow user some 'wiggle' so the recognizer
// doesn't fail if they move slightly while touching

longPressGR.allowableMovement = 80

longPressGR.minimumPressDuration = 1 // in seconds
longPressGR.numberOfTouchesRequired = 2

digitalTouchView.addGestureRecognizer(longPressGR)

Make a View Draggable

Ever wanted to enable a view to be dragged around the screen? A pan gesture recognizer does the trick:

func handlePanGesture(gr: UIPanGestureRecognizer) {
  guard gr.state != .Ended && gr.state != .Failed else { return }

  gr.view!.center = gr.locationInView(gr.view!.superview!)
}

let panGR = UIPanGestureRecognizer(
  target: self,
  action: "handlePanGesture:"
)

panGR.minimumNumberOfTouches = 1
panGR.maximumNumberOfTouches = 1

moveableView.addGestureRecognizer(panGR)

Topics

#17: UIKeyCommand πŸ”‘βŒ˜

Topics

iOS has had support for hardware keyboard shortcuts for a while now. New in iOS 9, Apple has made some great improvements to how apps can take advantage of them. Your app can now register keyboard shortcuts per view controller, and all currently valid shortcuts will be neatly
summarized when the user brings up the new keyboard shortcuts view.

Wiring up a new keyboard shortcut in this system couldn't be easier. Just define a new function, create a new UIKeyCommand and then call addKeyCommand on your view controller:

// inside some UIViewController subclass:

override func viewDidLoad() {
  super.viewDidLoad()

  let shortcut = UIKeyCommand(
    input: "n",
    modifierFlags: UIKeyModifierFlags.Command,
    action: "createNewFile:"
  )

  addKeyCommand(shortcut)
}

func createNewFile(command: UIKeyCommand) {
  // do the thing
}

Topics

#16: UIStackView 🚦πŸš₯

Topics

UIStackView changes everything. New in iOS 9, you can think of UIStackView as an abstraction layer on top of Auto Layout. It arranges it's subviews and manages their constraints for you.

One way to think about UIStackView is as the UIKit-born cousin to WKInterfaceGroup, the main layout component for building Apple Watch apps with WatchKit.

UIStackViews layout their arranged subviews horizontally or vertically, and they provide options for how arranged subviews should be aligned and distributed.

You can of course nest many UIStackViews inside each other to easily and quickly create complex layouts. Let's test it out by creating something super common like a UITableViewCell to display a comment. We'll use 2 horizontal and 1 vertical UIStackView:



UIStackView Pro Tips

  • πŸŒ€ Set views to hidden for easy conditional layout
  • πŸƒ Hide views in animation blocks to animate the re-layout
  • 🎳 Use size-classes to easily alter layout for different environments

Animating between two sets of Auto Layout constraints is quite simple.

All you have to do is update your installed/configured constraints and then call layoutIfNeeded inside of a UIView.animateWith* closure.



class DoorsViewController: UIViewController {
    var open: Bool = false {
        didSet { transition() }
    }

    func transition() {
        self.view.layoutIfNeeded() // force layout before animating

        UIView.animateWithDuration(0.4) {
            // change constraints inside animation block
            self.updateConstraintsForDoors()

            // force layout inside animation block
            self.view.layoutIfNeeded()
        }
    }

    func updateConstraintsForDoors() {
        leftDoorHorizontalConstraint.constant = open ? -16 : -leftDoorView.bounds.size.width
        rightDoorHorizontalConstraint.constant = open ? -16 : -rightDoorView.bounds.size.width
    }
}

Download sample project

SpriteKit is a phenomenal 2D game engine that ships in iOS and OS X. It has brought on a sort of renaissance amongst both amateur and professional game developers.

But what about using SpriteKit for fun, (and profit πŸŽ‰), and for things other than making games? What if we used SpriteKit to add special effects and final touches to our user interfaces?

class HeartBubblesScene : SKScene {
  var emitter: SKEmitterNode?

  override func didMoveToView(view: SKView) {
    // make scene's size == view's size
    scaleMode = .ResizeFill
    backgroundColor = UIColor.whiteColor()
  }

  func beginBubbling() {
    if let e = emitter {
      e.resetSimulation()
    } else {
      emitter = SKEmitterNode(fileNamed: heartsFile)

      let x = floor(size.width / 2.0)
      let y = heartHeight

      emitter!.position = CGPointMake(x, y)

      emitter!.name = "heart-bubbles"
      emitter!.targetNode = self

      addChild(emitter!)
    }
  }
}
class PotentialMatchViewController: UIViewController {
  @IBOutlet weak var heartBubblesView: SKView!
  let heartBubblesScene = HeartBubblesScene()

  override func viewDidLoad() {
    super.viewDidLoad()

    heartBubblesView.presentScene(heartBubblesScene)
  }

  @IBAction func interestedTapped(sender: UIButton) {
    heartBubblesScene.beginBubbling()
  }
}

Here we're adding an awesome finishing touch to this pretend dating app. When the user taps the i'm interested button, we trigger an SKEmitterNode.

Download sample project

Topics

#1: View Controller Initialization πŸš€

Topics

So, we're crafting our brand new view controller and BAM! we see this:



Let's try to break down how we got here:

Subclasses only inherit their superclass's initializers if certain conditions are met.

If our subclass doesn't define any designated initializers, it automatically inherits all of its superclass's designated initializers.

If not though, then it inherits none of them.

We subclassed UIViewController.

UIViewController implements NSCoding.

NSCoding requires init(coder:) be implemented.

By default UIViewController implements init(coder:) for us, and we don't need to do a thing.

But now, we defined our own new designated initalizer called init(), so we've stopped meeting the conditions for inheriting all of our superclass's designated initializers, and now have to override, implement and properly call super on init(coder:) all by ourselves!

Page 9 of 9