Per-View Auto-Rotation Locking Made Easy for iOS 8 and 9

This week I was working on an app where the client wanted to lock the app rotation to Portrait on some screens and Landscape on others. Luckily I had built all of my views in AutoLayout so they already supported the required layouts, I just needed to lock them. Rotation API’s are one of the most frequently deprecated parts of UIKit so when I started working on this, I had to look it up, and let me say, it’s pretty noisy out there. After an hour or so of research and another hour or two of experimentation, I finally boiled it down to two parts.

Select ALL POSSIBLE interface orientations you plan to support in your Info.plist.

Screenshot

Then you really only have to implement one method.

Swift 1.2

override func supportedInterfaceOrientations() -> Int {
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

Swift 2.0

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}

Making sure to select Portrait or Landscape depending on what you want. This is probably one of the messiest API transitions I have seen recently and it took me a while to actually notice what I kept doing wrong in Swift 1.2. (that Int cast is ugly)

Sample Code


If-Let Assignment Operator

This article was updated on 12/06/2017 to account for Swift 4

How many times have you had to implement this pattern?

if let value = someOptionalValue as? String {
    self.value = value
}

I use this all the time when parsing through JSON or implementing NSCoding and I think its a little over verbose for Swift, I felt sure there was a better way. NSHipster mentions a logical OR assignment operator (||=) which would be perfect however, it doesn’t seem to be implemented for generics (Please let me know if I am wrong here). I thought I would give it a try…

precedencegroup AssignmentPrecedence {
    associativity: right
}

infix operator ?= : AssignmentPrecedence

func ?=<T>(left: inout T, right: T?) {
    if let value = right {
        left = value
    }
}

It actually worked quite well, I was able to reduce the original code to this

self.value ?= someOptionalValue as? String

Might not be the biggest win, but when you have several of these assignments in a row, it saves a lot of code and makes it much more readable. One more thing… and I am still trying to figure out exactly what is going on here, but I ended up having to define a second function to assign to optionals. The only difference is the left parameter now is T?

func ?=<T>(left: inout T?, right: T?) {
    if let value = right {
        left = value
    }
}

var someOptionalString: String?

someOptionalString ?= newValue // Will assign when newValue is not optional

If you are interested in seeing this in action, here is the Playground


Inheriting Equatable in Swift

Today, I was working on creating some Swift objects that needed to conform to the Equatable protocol. Generally this is quite easy as you just implement the == function for the class.

func ==(rhs: , lhs: ) -> Bool {
    return 
}

The issue is these functions are required to be global (that’s the way operator declarations work in Swift), so there is no easy way to inherit your equality from super classes. Here is an incredibly contrived example:

class Person: Equatable {
    var name: String = ""
}

class Employee: Person {
    var position: String = ""
}

func ==(lhs: Person, rhs: Person) -> Bool {
    return lhs.name == rhs.name
}

func ==(lhs: Employee, rhs: Employee) -> Bool {
    return lhs.name == rhs.name && lhs.postion == rhs.position
}

Say we want to implement Equatable for both of these classes. We will want Equatable for Person objects to mean they have the same name (this is really all we have in this class, but pretend it was more complicated). Employees can have the same name, but their equality will also depend on their position (manager, worker, owner, etc..). We don’t want to have to reimplement the Person == functionality in our subclass, usually you would want to call to super, but since Swift operators are global, you have no sense of super. The solution is actually quite simple when you think about it. You want to move the Equatable logic into the classes where they can call to super. Just have your == function call into equalTo() on your instances, you can create it like this.

func ==(lhs: Person, rhs: Person) -> Bool {
    return lhs.equalTo(rhs)
}

func equalTo(person: Person) -> Bool {
    return self.name == person.name
}

Now you can then easily call the equalTo() function in your parent class. Here is the the completed solution, and Playground.

func ==(lhs: Person, rhs: Person) -> Bool {
    return equalTo(rhs)
}

func ==(lhs: Employee, rhs: Employee) -> Bool {
    return lhs.equalTo(rhs)
}

class Person: Equatable {

    var name: String = "Bob"

    func equalTo(person: Person) -> Bool {
        return self.name == person.name
    }
}

class Employee: Person {

    var position: String = "Manager"

    func equalTo(person: Employee) -> Bool {
        var match = super.equalTo(person)
        match = match && self.position == person.position
        return match
    }
}

This was tested on Swift 1.2

UPDATE 11/6/2015: Creating an isEqual function on NSObject subclasses was causing issues, changing to equalTo() fixes this.


NSCoding class chaining with Swift

While I was trying to get some Swift classes to conform to NSCoding so that I could serialize them, I found out that it can be a little annoying to get the init(coder:) calls to chain through your class hierarchy. All of the resources I found online suggested that my initializers need to be declared as convenience, however if I declared them as such, they would complain that you need to call self’s designated initializer. I could remove the convenience, but this is quite annoying because it forced me to do all of my initialization with a coder. After a little research, I discovered that I needed to remove the convenience AND make sure I also override the default init. I believe the issue is that the default init is marked as designated and forces your subclasses to call init(), if you override without required, you can call a different initializer from a subclass. Here is a short example and a Playground

class Person: NSObject, NSCoding {

    var name: String = "Bob"

    override init() {
        super.init()
    }

    required init(coder aDecoder: NSCoder) {
        self.name = aDecoder.decodeObjectForKey("name") as! String
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(self.name, forKey: "name")
    }
}

class Employee: Person {

    var position: String = "Manager"

    override init() {
        super.init()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.position = aDecoder.decodeObjectForKey("position") as! String
    }

    override func encodeWithCoder(aCoder: NSCoder) {
        super.encodeWithCoder(aCoder)
        aCoder.encodeObject(self.position, forKey: "position") 
    }
}

var person = Employee()
person.name = "Bill"
person.position = "Worker"

print(person.name)
print(person.position)

let data = NSKeyedArchiver.archivedDataWithRootObject(person)
let newPerson = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? Employee

print(person.name)
print(person.position)

This has been tested with Swift 1.2


Remove that shadow!

UIKit can be so frustrating sometimes. I was trying to remove a shadow from UINavigationBar, and it turns out there is a property for doing this…

navigationBar.shadow = &lt;something&gt;

Perfect! or not… Apparently, it only works if you have a custom image as your bar background

The default value is nil, which corresponds to the default shadow image. When non-nil, this property represents a custom shadow image to show instead of the default. For a custom shadow image to be shown, a custom background image must also be set with the setBackgroundImage:forBarMetrics: method. If the default background image is used, then the default shadow image will be used regardless of the value of this property.

So, I cannot use a solid colored background without a shadow… after looking around online, I found bunch of extensions on UIImage that returns an image of a given size and color, but I felt that custom drawing was a little overkill here. My solution is as follows:

extension UINavigationBar {
    
    func removeShadow() {
        if let view = removeShadowFromView(self) {
            view.hidden = true
        }
    }
    
    func removeShadowFromView(view: UIView) -> UIImageView? {
        if (view.isKindOfClass(UIImageView) && view.bounds.size.height <= 1) {
            return view as? UIImageView
        }
        for subView in view.subviews {
            if let imageView = removeShadowFromView(subView as UIView) {
                return imageView
            }
        }
        return nil
    }   
}

This way, I simply call

navigationController?.navigationBar.removeShadow()

No Shadow

and all is right with the world. Feel free to use this extension in your own work, it’s nothing particularly genius, but it sure helps me out. This is based on some Objective-C code I found here

Update for iOS 10: I changed the above code so it “hides” the shadow, rather than removing it. This is confirmed working on both iOS 9 and 10. The removeFromSuperview() method doesn’t work on iOS 10.