Freelance iOS Developer, Rapidly Aging Punk

Facebook Pop Completion Block Syntax in Swift

Dec 29, 2014 at 01:06PM

For my own reference, mostly:

  myPopAnimation.completionBlock = {(animation, finished) in
            //Code goes here
    }

What to Do When Your VFL Constraints Aren't Respecting Margins in iOS8

Dec 23, 2014 at 03:42PM

Let's say you've got a view with a single button, and you want that button to sit at the top center of the view. You set up the constraints using constraintsWithVisualFormat: like so:

- (void)layoutSubviews {
    [self addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|-[_button]"
                            options:0
                            metrics:nil
                              views:@{@"_button" : _button}]];

    [self addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|-[_button]-|"
                            options:0
                            metrics:nil
                              views:@{@"_button" : _button}]];

    [super layoutSubviews];
}

In iOS7 you get exactly what you want:

Result in iOS7

In iOS8 you end up with a button smooshed underneath the status bar:

Result in iOS8

What's going on? Apple changed the default behavior of how AutoLayout respects margins in iOS8. So now when you use V:|-[_button] AutoLayout says "put a default amount of space between the superview and _button, with no respect to the margins" where previously it would have first taken the margins of the superview into account.

There are a few things you can do about this. First there's a new property of UIView called preservesSuperviewLayoutMargins which is set to NO by default. On paper this seems like just the thing you need, but in testing I couldn't figure out what changing this to YES actually did. So scrap that one.

Next, you can manually set the edge insets of your superview like so:

- (UIEdgeInsets)layoutMargins {
    return UIEdgeInsetsMake(22, 0, 0, 0);
}

This works! But I really hate setting that top margin to a literal value. It seems safe enough now, but the old way seemed pretty safe too until it wasn't.

What we end up doing is setting the constraint to use the topLayoutGuide property of the view controller instead of referencing the superview directly:

NSDictionary *views = @{ @"_button" : _button,
                         @"_topLayoutGuide" : _viewController.topLayoutGuide };

    [self addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:[_topLayoutGuide][_button]"
                                options:0
                                metrics:nil
                                views:views]];

This works in both iOS7 & iOS8. The only kludgy part is that you need to set a property on your view that references back to the view controller so you can retrieve the topLayoutGuide, but you can do that easily enough in an initializer.

You can see a complete example project here: https://github.com/richrad/AutoLayoutVFLMargins


Google Orion Slave Girls

Dec 21, 2014 at 01:19AM

Google's blog post today is a transparent attempt to deflect focus from its own conduct and to shift attention from legitimate and important ongoing investigations by state attorneys general into the role of Google Search in enabling and facilitating illegal conduct – including illicit drug purchases, human trafficking and fraudulent documents as well as theft of intellectual property.

(Emphasis mine.)

This is news to me. What's the exact search term I have to use to find a purchasable human on Google? It's hard enough just to use Google to steal a movie without ending up with a downloads folder full of questionable .exe files.


Dave Winer: Zuck at 22 was wrong

Dec 20, 2014 at 07:58PM

Dave Winer on that dumb thing Mark Zuckerberg said when he was 22:

My own experience: I did great work when I was in my 20s, 30s, 40s and 50s. The stuff I'm most famous for I did in my 40s. But the best software I've ever done was this week. I think that's pretty much been true all the way through. It all builds.

I hope this is always true for me, too. The day I start thinking the code I wrote a month ago is still good will probably be the day to hang it up.


iPad Style Popovers on the iPhone with Swift

Nov 28, 2014 at 02:34PM

Xcode has a built-in adaptive segue called "Present As Popover" but by default it only behaves the way you'd expect it to — presenting a view in a speech-bubble style floating popover — on the iPad. On the iPhone your views are presented modally, full-screen. This is on purpose and is yet another subtle encouragement from Apple that we should build our apps with adaptivity in mind, but it's almost never what I actually want my popovers to do. Here's how to fix that.

Drag a new view controller onto your storyboard and tweak these settings:

Simulated Metrics Under "Simulated Metrics" change "Size" to "Freeform" and "Status Bar" to "None."

Simulated Size Under "Simulated Size" change your view's height and width to the actual size you want your popover's content to be.

Content Size Back on the attributes inspector check the box next to "Use Preferred Explicit Size" and make sure the height and width match the sizes you set in the previous step.

Drag a new segue from a control on your presenting view controller (a UIBarButtonItem is a good fit) to your popover view controller. Choose "popover presentation" from the dropdown and then give your segue an ID:

Segue ID

Open your view controller class file and add the UIPopoverPresentationControllerDelegate protocol:

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {

Override the prepareForSegue function to catch your popover segue. Set the modalPresentationStyle to .Popover to explicitly state that you want a popover and then assign the delegate property of the view's popoverPresentationController to self:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "popoverSegue" {
            let popoverViewController = segue.destinationViewController as! UIViewController
            popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
            popoverViewController.popoverPresentationController!.delegate = self
        }
    }

Finally, implement the adaptivePresentationStyleForPresentationController function to tell your app that you really want that popover presentation and will accept no substitutions:

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
        return UIModalPresentationStyle.None
    }

Hello, there!

That's it.

Edited 4/24/15: Changed an as to an as! for Swift 1.2