Seichy's Missing Bit

Using TableViewControllers with Swift

Using a TableViewController with swift is easy. It takes only a couple of steps to create a table with custom cells. I started by creating a single view application.

New Project

Setting up the table and cell prototype

I started by deleting the default view. Then, I added a Table View Controller to the storyboard. Finally, I added two labels to the prototype cell. You can use the prototype cell to define how the cells for the table will look, so go ahead and tweak it to your convenience. Afterwards, I set the cell's identifier to 'cell'. I am creating a Twitter client so I only need to display the user and the Tweet.

(There's a preset style that's only a title and a subtitle which I could have used but, I want more control over it's appearance so I went this way)

Once I had my cell and table ready I just needed to do a couple more things. First, I created a class called TweetCell that inherits from UITableViewCell. I'll be using this class as a base for my custom cell. I set my class as the custom class and I added two outlets to refer to both of the labels. I named the big one "username" and the smaller one "body".

// TweetCell.swift
import UIKit

class TweetCell: UITableViewCell {

    @IBOutlet weak var username: UILabel!
    @IBOutlet weak var body: UILabel!
}

The ViewController

Now I only have to set up the view controller. We already have the basic view controller that came with the project so now we can just change it to do our bidding.

I start by creating a struct to contain my tweets. I added it before the ViewController's class definition.

struct Tweet {
    var username: String
    var body: String
}

After that, I changed the ViewController's parent class from UIViewController to UITableViewController and added the UITableViewDataSource protocol to it so my class looks like this:

class ViewController: UITableViewController, UITableViewDataSource {

}

Now I added an array with a couple of tweets:

var tweets: [Tweet] = [
    Tweet(username: "Seich", body: "Hello, World"),
    Tweet(username: "Albert", body: "How are you doing?")
]

Finally I just need to override a couple of methods. The first one is this method: tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! which determines what cell should be drawn at the given index. Here's how mine looks:

override func tableView(
    tableView: UITableView!, 
    cellForRowAtIndexPath indexPath: NSIndexPath!) 
    -> UITableViewCell! {

    var cell = tableView.dequeueReusableCellWithIdentifier("cell") as TweetCell

    cell.username.text = tweets[indexPath.row].username
    cell.body.text = tweets[indexPath.row].body

    return cell
}

The second method is this one: override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int which should return the number of cells to draw, mine looks like this:

 override func tableView(
    tableView: UITableView!, 
    numberOfRowsInSection section: Int) -> Int {

    return tweets.count
}

And that's it. Here's my final code for the view controller:

import UIKit

struct Tweet {
    var username: String
    var body: String
}

class ViewController: UITableViewController, UITableViewDataSource {

    var tweets: [Tweet] = [
        Tweet(username: "Seich", body: "Hello, World"),
        Tweet(username: "Albert", body: "How are you doing?")
    ]

    override func tableView(
        tableView: UITableView!,
        numberOfRowsInSection section: Int)
        -> Int {

        return tweets.count
    }

    override func tableView(
        tableView: UITableView!,
        cellForRowAtIndexPath indexPath: NSIndexPath!)
        -> UITableViewCell! {

        var cell = tableView.dequeueReusableCellWithIdentifier("cell") as TweetCell

        cell.username.text = tweets[indexPath.row].username
        cell.body.text = tweets[indexPath.row].body

        return cell
    }
}

And this is what my finalised app looks like:

Final app

It's not perfect but, it shouldn't take much to have it looking decent. I'll probably be revisiting this soon and finishing the whole Twitter application so stay tuned for more.

Resources

About Table Views in iOS Apps

Creating and Configuring a Table View

Storyboard Tutorial

Using MapKit with Swift

I've been using Swift a lot lately. I wanted to start making iOS apps a while ago but Objective-C is frankly, not worth the trouble. Swift on the other hand is actually fun to use and I love experiencing how the language evolves.

This weekend I started working on a small app I've wanted for a while but got stuck when it came to implementing a map with MapKit. I couldn't find documentation or code samples on how to do it anywhere. Turns out that it's not hard at all! It took very little fiddling around before I figured it out. Here's how I did it:

First, create a new project

I started with a Single View Application.

New Project Screen Then added a MKMapView to the view controller. After that, imported MapKit into the view controller's code. Finally, I added a new reference outlet to the map called map.

Setup your desired location

Now our map is ready to display a location. You'll need a couple of things to set it to the right place. First, you need a location in the form of a latitude and longitude and a CLLocationCoordinate2D.

var location = CLLocationCoordinate2D(
    latitude: 16.40, 
    longitude: -86.34
)

Now, we need to tell the map what the area spanned by the region is. For this we use MKCoordinateSpanMake. Which, as you probably guessed, creates a MKCoordinateSpan.

var span = MKCoordinateSpanMake(1, 1)

After that we just need to define the region using these two things:

var region = MKCoordinateRegion(center: location, span: span)

And finally, we set this region to the map:

map.setRegion(region, animated: true)

Bonus: Adding an annotation

Adding an annotation is actually quite easy. We start by creating a new MKPointAnnotation instance. It'll display the default red pin which, might not be as pretty but it'll get the job done.

var annotation = MKPointAnnotation()

Afterwards, we can set it's coordinates, title and subtitle to place it wherever we want and to add the text it's callout will display when tapped.

annotation.setCoordinate(location)
annotation.title = "Roatan"
annotation.subtitle = "Honduras"

Finally, we add it to the map like this:

map.addAnnotation(annotation)

Here's my final code:

import UIKit
import MapKit

class ViewController: UIViewController {

    @IBOutlet weak var map: MKMapView!
    override func viewDidLoad() {
        super.viewDidLoad()

        var location = CLLocationCoordinate2D(
            latitude: 16.40, 
            longitude: -86.34
        )

        var span = MKCoordinateSpanMake(0.5, 0.5)
        var region = MKCoordinateRegion(center: location, span: span)

        map.setRegion(region, animated: true)

        var annotation = MKPointAnnotation()
        annotation.setCoordinate(location)
        annotation.title = "Roatan"
        annotation.subtitle = "Honduras"

        map.addAnnotation(annotation)
    }
}

And here's what my app looks like: Final product

Additional Resources

Location and Maps Programming Guide

Using mailgun to receive emails with Phabricator

Phabricator is awesome. We recently started trying it out for 2build.it (more like, this morning) and we all (Jorge and I) agreed it's awesome.

The setup is pretty straight-forward and only took like 20 minutes. The one thing I got stuck on though, was the emailing. It took me forever to realise that you're supposed to restart the emailing daemons for the configuration to stick. It is my fault but, this wasn't mentioned anywhere in the documentation. Btw, here's how you do it:

./bin/phd restart

Right from the Phabricator folder.

Moving on.

Now that sending was settled there was one huge thing that wasn't working. Inbound emails. I tried working with postfix but, it was too painful and after a couple of hours I didn't get anywhere. The documentation talks about using sendgrid to achieve it but, I already have a mailgun account and according to the configuration Phabricator should be able to use mailgun as well. So, I had to do some research.

It turns out you can use mailgun to do this but, there's zero documentation about it. Luckily, I found the commit where this functionality was added and figured it out on my own. Here's the three step guide to getting it done.

Step 1

You have to configure you domain's MX records to point to mailgun. You can find more information here.

Step 2

Go to your setup your mailgun configuration by going to the mailgun config (http://example.com/config/group/mailgun/) and adding your API key and your domain.

Step 3

We're almost done. Now, all we have to do is tell mailgun to send received emails your way. To do this all you have to do is create a new route (go here: https://mailgun.com/cp/routes). Give it a priority and a filter that'll work for you (I used 0 as a priority and catch_all() for a filter). For the action, forward it to your Phabricator installation with the endpoint '/mail/mailgun'. Like this: forward("http://example.com/mail/mailgun/").

That is all. Your emails should now allow you to interact with Phabricator.

Sketch Presets for Desktop Screens

I've been using Sketch to create mock ups for clients lately. So far so good. Today, I wanted to create art board presets for common desktop screen sizes. It took me a little while to find out how to organize them into folders. Luckily for me, Jan Drewniak has a blog post detailing how to do just this.

Sketch actually keeps these artboard presets in an artboards.sketchpreset file located somewhere on your mac. If you're using the App Store version of Sketch, then the file is located here:

~/Library/Containers/com.bohemiancoding.sketch/Data/Library/Application  Support/sketch/

Here's my presets:

<dict>
    <key>name</key>
    <string>Desktop</string>
    <key>presets</key>
    <array>
        <string>Retina Displays</string>
        <dict>
            <key>width</key>
            <integer>2880</integer>
            <key>name</key>
            <string>15-inch</string>
            <key>height</key>
            <integer>1800</integer>
        </dict>
        <dict>
            <key>width</key>
            <integer>2560</integer>
            <key>name</key>
            <string>13-inch</string>
            <key>height</key>
            <integer>1600</integer>
        </dict>
        <string>4:3</string>
        <dict>
            <key>width</key>
            <integer>1600</integer>
            <key>name</key>
            <string>Large 4:3</string>
            <key>height</key>
            <integer>1200</integer>
        </dict>
        <dict>
            <key>width</key>
            <integer>1280</integer>
            <key>name</key>
            <string>Medium 4:3</string>
            <key>height</key>
            <integer>1024</integer>
        </dict>
        <string>16:9</string>
        <dict>
            <key>width</key>
            <integer>1920</integer>
            <key>name</key>
            <string>Large 16:9</string>
            <key>height</key>
            <integer>1080</integer>
        </dict>
        <dict>
            <key>width</key>
            <integer>1600</integer>
            <key>name</key>
            <string>Medium 16:9</string>
            <key>height</key>
            <integer>900</integer>
        </dict>
        <dict>
            <key>width</key>
            <integer>1366</integer>
            <key>name</key>
            <string>Small 16:9</string>
            <key>height</key>
            <integer>768</integer>
        </dict>
    </array>
</dict>

I have categories for: Retina, 16:9 and 5:4 displays. Only listing the ones I've used recently and might re-use in the future.

Scroll Spying for Fun and Profit

I recently redesigned by blog. One of the new features is a menu that highlights the current post you are on.

To achieve this, I used Zepto and a small amount of math. (The great thing about Zepto is that it's fully compatible with jQuery, so this should work with jQuery out of the box as well).

Let's jump right in, here's how I do it:


$(window).on('scroll', function() {
    var scrollTop = $(window).scrollTop();
    var article_offsets = $('article').map(function(i) { 
        if (i === 0) {
            return 0;
        }

        return $(this).offset().top 
    });

    for (var i = 0; i < article_offsets.length; i++) {
        if (article_offsets[i] - 40 <= scrollTop) {
            $('.sidebar li').removeClass('active')
                .eq(i).addClass('active');
        }
    };
});

The first line of interest:

var article_offsets = $('article').map(function(i) { 
    if (i === 0) {
        return 0;
    }

    return $(this).offset().top 
});

Grabs all the vertical positions (from now on, offsets) of the elements I want to match and makes an array out of them. In this case, I am targeting the articles since, those represent my blog posts. I make sure to mark the first one as 0, so it works when the page first loads at scroll position 0.

After doing this, we iterate over all our scroll positions, if we find ourselves scrolled after one of the offsets we run this:

$('.sidebar li').removeClass('active').eq(i).addClass('active');

This looks for the Ith element on the sidebar's list and we add a class of 'active' to it, highlighting it and doing whatever we want with it.

There's a couple of other (bigger, more complex and probably more robust) solutions to this problem out there. Here's a couple I came through:

I am pretty sure they are plenty of others but, for my evil purposes, my tiny script fulfills my needs.