chsmc.org

Default constraint behaviors using Swift protocols

When developing an iOS app, you often need to adjust constraints in order to make sure that the keyboard doesn’t obscure any of the elements on screen. This is a common problem that is an easy but annoying fix.

A few days ago I came across this Medium article by Roy McKenzie about a Swift protocol called KeyboardAvoidable that makes this process so much easier.

To sum it up quickly, any view controller that needs to adjust constraints in response to the keyboard hiding/showing just needs to conform to this protocol and then provide an array of constraints that need to be adjusted. The protocol extension has default methods that can be called when the controller is presented to add keyboard observers, and vice versa when the controller is dismissed.

With this short bit of code (available ion a Gist at the end of the post), all you would need to do to get this behavior is hook up outlets to the constraints of the views that need to be adjusted (probably the bottom constraint of a scroll view), stick them in an array, and implement the requirements of the protocol. When the keyboard is shown, your constraints will all be adjusted and animated. Sweet.

This ideas is so incredibly cool and useful and awesome and I plan to use it in every single project that requires this kind of behavior. What’s better is that the code is very easy to understand and modify for your specific needs.

After seeing this, it got me thinking about other ways protocols can be used to add default behaviors to views by injecting constraints. Every constraint you create in IB is of type NSLayoutConstraint, which means we can create very generic and reusable code very easily. Natasha the Robot has a great post about protocol-oriented views in Swift that is similar to this, except she isn’t using constraints. In that post, she demonstrated adding animations like shaking to views using protocols so that this functionality can be reused.

In a project I am working on, I have some views inside of a view controller that need to be toggled between being hidden or shown when the user tap’s a button. In addition, I want the view to animate into and off of the screen when it is toggled.

Originally, I was just creating outlets to the constraints on these views that I wanted to collapse upon and then putting all of the toggling logic into a method in my view controller that would get called when a button was tapped. This led to a lot of repeated code. For every collapsible view in my view controller, I was essentially writing the exact same code with slight variations to change which constraint I was collapsing upon. After seeing Roy’s KeyboardAvoidable protocol, I realized there was a much better way.

I started by making a protocol to represent collapsible views:

protocol Collapsible {
  var collapseConstraint: NSLayoutConstraint? { get set }
  func collapseView()
  func showView()
  func isCollapsed() -> Bool
}

The collapseConstraint variable is the constraint that we want our view to collapse upon. I made this optional because there could be a situation where we want to use one of these views without the collapsing functionality, and in that case we just won’t set this variable and it will default to nil. The collapseView() and showView() methods are called when we tap our button, and the isCollapsed() method just returns a bool letting us know what state we’re in.

Next, I created an extension for my Collapsible protocol that defined my default implementations of those methods. I constrained my extension to only apply to UIView objects:

extension Collapsible where Self: UIView {
  func collapseView() {
    collapseConstraint?.constant = -(self.frame.size.width)
  }

  func showView() {
    collapseConstraint?.constant = 0
  }

  func isCollapsed() -> Bool {
    return !(collapseConstraint?.constant == 0)
  }
}

In my app, these view’s will be sliding into and off of the screen from the left or the right, so the collapseView() method set’s the constant of the collapse constraint to the negative value of the width (that way the view is entirely off screen). The showView() method sets the constant to 0, so that the view is pinned to the left or right edge. These methods will obviously need to be customized depending on which direction you want your view’s to collapse. If you wanted to get really fancy, you could set the direction as well as the constraint so that you can collapse in any direction with this one protocol.

The next step is just to create a view that conforms to the protocol, and declare our collapseConstraint variable:

class MyView: UIView, Collapsible {
  var collapseConstraint: NSLayoutConstraint?
}

In my app, I am putting my views into my controller using Interface Builder. So inside of my controller I just create an outlet to the view and to the constraint that I want to collapse upon (trailing for right edge or leading for left edge), and in viewDidLoad() I set the collapseConstraint variable:

class MyViewController: UIViewController {
  @IBOutlet var myView: MyView!
  @IBOutlet var myViewLeadingConstraint: NSLayoutConstraint!

  override func viewDidLoad() {
    super.viewDidLoad()

    myView.collapseConstraint = myViewLeadingConstraint
  }
}

The only thing left to do is put in a method that gets called when you tap a button (or take some other action):

func buttonTapped() {
	if myView.isCollapsed() {
	  myView.showView()
	}
	else {
	  myView.collapseView()
	}

	UIView.animateWithDuration(0.3) {
	  self.view.layoutIfNeeded()
	}
}

I put in the animation block in order to make the constraint change animate over a given time.

Building protocols like these make creating repeated behaviors extremely simple, and IB constraints fit so perfectly into this method. I am using this technique in several places, and I highly recommend it.

Applying Pixar’s rules of storytelling to writing

There has been a lot of talk about this article, which outlines Pixar’s 22 rules of storytelling. They are all great tips, but a few of them in particular stand out as all-purpose rules that not only apply to animation, but also writing.

You gotta keep in mind what’s interesting to you as an audience, not what’s fun to do as a writer. They can be very different.

When first deciding what I should write, I first ask myself what I would enjoy reading. If you write about something that you are interested in, your writing will benefit. Truly caring about your subject matter is the first step to making great stuff.

Sometimes writers are tempted to write what would be easier. Instead, they should write about what they would want to read. Because whoever reads your stuff does so because they are interested in the same things as you are. Writing about your interests will benefit your writing and your readers.

Come up with your ending before you figure out your middle. Seriously. Endings are hard, get yours working up front.

The most important part of writing is to have a message. Deciding what you want to say before you say it is essential to making a clear point. Using as few words as possible to convey the meaning of your words is almost always the way to go. I hate reading writers who beat around the bush before they finally make their point. Be clear and concise and your message will come across much stronger.

Also, be sure that you have an ending to write. Have a point to make. Have an end goal to accomplish. That will make writing everything before the ending a lot easier.

Why must you tell THIS story? What’s the belief burning within you that your story feeds off of? That’s the heart of it.

Although having something to write about is important, it’s also important to understand why you want to write about your topic in the first place. Why is this something you need to tell people? Why would they even care? Motivated writing is good writing. Don’t just have something to say, have the reason and desire to say it.

No work is ever wasted. If it’s not working, let go and move on – it’ll come back around to be useful later.

If you write something and decide to scrap it because you think it is terrible, do not feel as though you have wasted your time. Writing is hard. And the only way to become good at writing is to write. No matter what you are writing, good or bad, it is practice in expressing your thoughts and ideas. It is all worth it.

A good way to practice writing without worrying about quality is to keep a journal. I like to use Day One.

Writing is essentially storytelling, and the people at Pixar are some of the best storytellers I know. Therefore, it would be very wise to apply their principles to our work.

There is a quote that says “The man who can read and chooses not to is no better than the man who cannot read at all.” If we don’t seek to better ourselves and our work we can never hope to set ourselves apart from not only others in our respective fields, but anyone at all. If you write, you have to make an effort to get better. Learning from others is the best way I can think of to do that.