Active Filters: B-sides

Topics

#303: Editing Videos in UIKit 📼

Topics

Today we're continuing our hunt for hidden gems in UIKit with UIVideoEditorController.

It's a cousin to UIImagePickerViewController that exposes just the basic video editing functionality from that class in a standalone, dedicated video editing view controller.

Let's give it a try.

First, we'll need a video file to edit. We'll use this one which is part of a freely available collection.

We'll download the video, rename it to something simple, and drag it into Xcode. We'll check the box next to our app in the dialog that appears, so it gets copied to our app target.

Now, let's write some code.

First, we'll make sure our video can be found:

guard let path = Bundle.main.path(forResource: "video", ofType: "mp4") else { return }

...and that UIKit knows how to edit it:

guard UIVideoEditorController.canEditVideo(atPath: path) else { return }

Nice. Next, we can create a new video editor view controller and configure a few things on it:

let editor = UIVideoEditorController()

editor.videoPath = path
editor.videoMaximumDuration = 10.0
editor.videoQuality = .typeIFrame1280x720

Here we've told it our path, and given it a 10 second max duration. (Pro Tip: Default is 10 minutes, set to 0 for no max).

From here we can simply present it like any other view controller:

present(editor, animated: true, completion: nil)

Neat!

The best part is all the functionality is self-contained inside the view controller.

The user can scrub through:

Trim the edges:

Then save the video back to the videoPath we set earlier:

Note: The documentation mentions UIVideoEditorController "only supporting Portrait" orientations, but it seems to work fine in all orientations.

Last but not least, we can (optionally) set a delegate on our UIVideoEditorController to get notified when the user saves or cancels (or a save fails):

editor.delegate = self

extension SomeViewController : 
    UIVideoEditorControllerDelegate, 
    UINavigationControllerDelegate {

  func videoEditorController(_ editor: UIVideoEditorController, 
    didSaveEditedVideoToPath editedVideoPath: String) {

    print("saved!")
  }
}

That's all for today. Know of an interesting UIKit B-side?

Send it on over!

Today we'll continue our series on finding hidden gems in UIKit with UIReferenceLibraryViewController.

Believe it or not there's a entire dictionary (yes like for viewing the definition of words/terms) just hanging out inside UIKit.

Let's give it a try! 📖

We can present a new reference library view controller for any word/term we want like this:

present(
  UIReferenceLibraryViewController(term: "Spaceship"),
  animated: true, 
  completion: nil
)

Very cool.

This works great, but things start to break down when we look for something and it's nowhere to be found:

We can fix this by checking if a term can be found before presenting, using the static dictionaryHasDefinition(forTerm:) function:

if UIReferenceLibraryViewController.dictionaryHasDefinition(forTerm: term) {
  present(
    UIReferenceLibraryViewController(term: term),
    animated: true, 
    completion: nil
  )
}

Much better.

UIReferenceLibraryViewController supports many different languages. New languages can be installed using the "Manage" option in the bottom-left:

Finally, it's worth noting that we get definition functionality for "free" in UITextFields via the "Look Up" item in the UIMenu that's shown when a user selects some text:

... and it's also worth noting that the view controller that appears when a user taps one of these "Look Up" menu items in a UITextField appears to be far more advanced than the what we get when presenting a plain ol' UIReferenceLibraryViewController:

The Dictionary results are still present, but we also get results across Music, Wikipedia, Movies, Websites, and even the App Store. Very cool.

(And nope, UIKit does not seem to provide a way to trigger this fancier view controller directly. Anyone interested in this should probably file a Radar.)

That's all for today. Those who want more UIKit B-Sides can check out these bites here.

Topics

#246: UITextField B-sides 📼

Topics

UITextField is one of the sort of "low key" hidden gems of UIKit. Today we'll look at some of the lesser-known things it can do and how to configure them. Let's dive in.

We'll begin by creating a text field and customizing its look and feel. First, instead of boring plain placeholder, let's spice things up a bit:

let placeholder = NSMutableAttributedString()

placeholder.append(
  AttributedString(
    string: "Spaceship ",
    attributes: [NSFontAttributeName : boldFont]
  )
)

placeholder.append(
  AttributedString(
    string: "Name",
    attributes: [NSFontAttributeName : font]
  )
)

textField.attributedPlaceholder = placeholder

Nice, now let's customize how our text field works.

Suppose we wanted to clear out its contents when a user tapped it. We simply set clearsOnBeginEditing to true.

Similarly, if we wanted to clear the contents not when the user tapped the field, but rather when the user began typing content, we can set clearsOnInsertion to true.

Let's add some more "padding" to our text field. This turns out to be a tricker than expected.

UITextField allows for customization of the rects it uses to draw text via subclassing:

class PaddedTextField : UITextField {
  override func textRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: 16.0, dy: 8.0)
  }
}

Finally, we'd like allow users to fancy up (bold, italic, underline) the names of their spaceships. Turns out UITextField makes this super easy:

textField.allowsEditingTextAttributes = true

With attribute editing enabled, we now get this great editing rich-text UI for free. Neat!

Topics

#154: Return of the UIKit B-sides! 📼

Topics

Today we're continuing our look at lesser-known UIKit features. Let's get started:

Fixing Unwanted Undo Prompts

Ever been testing an app and have an unwanted "Undo" prompt appear? This can be dynamically enabled or disabled with a property on UIApplication:

UIApplication.sharedApplication()
  .applicationSupportsShakeToEdit = false

Customizing Tab Bar Items Layout

We can use the UITabBar's itemPositioning property to control whether tab bar items use a "fill" or "centered" layout:

let tabBarController = UITabBarController()
tabBarController.tabBar.itemPositioning = .Centered

Significant Time Changes

The applicationSignificantTimeChange app delegate function lets us know when things like daylight savings time begin.

Refresh Control Labels

UIRefreshControl has an attributedTitle property we can use to show a nice little bit of text when a user pulls to refresh:

refreshControl.attributedTitle = attributedStringForRefreshControl()

In-App Dictionary View Controller

This one is awesome.

We can use a reference library view controller to display a nicely formatted screen depicting the definition of a term in our app. Neat!

let vc = UIReferenceLibraryViewController(term: "awesome")
showViewController(vc, sender: nil)

We're continuing our look at lesser-known UIKit functionality today with UITableView & UITableViewCell. Let's see what we can find:

Blur & Vibrancy Visual Effects

We can set UIVisualEffectView backgroundView, and a vibrancy separatorEffect so our table view really shines:

let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .Dark))

tableView.backgroundView = blurView
tableView.separatorEffect = UIVibrancyEffect(forBlurEffect: blurView.effect as! UIBlurEffect)

Row Actions

We can get those awesome "swipe-to-reveal" actions from Mail.app in our own table views. We just need to implement one delegate function and return an array of UITableViewRowActions.

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
  let deployAction = UITableViewRowAction(
    style: .Default,
    title: "Deploy"
  ) { (action, indexPath) in
    // TODO: Deploy the troop at this indexPath.row
  }

  return [deployAction]
}

Adjusting to State Transitions

We can override the willTransitionToState(state:) and didTransitionToState(state:) functions in our UITableViewCell subclasses to run code when the cell begins or finishes showing the edit control or delete confirmation:

class StormtrooperCell : UITableViewCell {
  override func didTransitionToState(state: UITableViewCellStateMask) {
    super.didTransitionToState(state)

    if state == .ShowingEditControlMask { print("began editing!") }
  }
}

Multiple Selection Background View

We can set a special background view on cells that will be shown only when we our table view supports multiple selection:

multipleSelectionBackgroundView = EditingView()

Topics

#151: UIView B-sides 📼

Topics

Today we'll begin looking at some lesser-known functionality inside UIKit. Some of these may be old news, but hopefully not all are.

We'll start with UIView. Let's flip it over and see what kind of b-side treasures we can find:

Subview Callbacks

We can override functions like didAddSubview(subview:) and willRemoveSubview(subview:) in our UIView subclasses. With this, we can take some action when subviews are added/removed.

class ContainerView : UIView {
  override func didAddSubview(subview: UIView) {
    super.didAddSubview(subview)

    subview.frame = calculateFrame(subview)
  }
}

Resigning First Responder

An oldie, but a goodie. We can use the endEditing(force:) function to ask (or force) a view or any text fields inside it to resign as the first responder:

view.endEditing(true)

Custom Layer Class

We can specify a custom CALayer subclass for our UIView subclasses by overriding the layerClass function. This is great if our view needs to use CATiledLayer to scroll lots of content, or when building a camera preview view:

class CameraPreviewView : UIView {
  override class func layerClass() -> AnyClass {
    return AVCaptureVideoPreviewLayer.self
  }
}

Easy Masking

We can "mask" a view using the alpha channel of another view by setting it to the maskView property:

view.backgroundColor = UIColor.redColor()
view.maskView = UIImageView(image: UIImage(named: "vader"))