Converting Objective C to Swift : The Circular Picker View Example

Screenshot 2015-03-01 14.59.10Swift is a new language. Objective C is not. Sometimes you have some Objective C code and want to make it into Swift code. I had a case of that this week when a reader asked me about continuous picker view wheels. If you are familiar with the date picker it goes from 0 to 59 and then loops back to 0, making a continuous wheel. The UIPickerView does not have this ability. When asked, I did not know how to do this. The reader found some code in Objective C that did that. I did a little web searching and I did find a solution. Of course it was in Objective C, not Swift.

I’ve wanted to write a piece on converting from Objective C to Swift for a while. I didn’t have a good example to use. This makes a really good example. We’ll discuss some things you should know and try an example of converting a method for a UIPickerView from Objective C into Swift.

Header Files and Scope

Objective C uses two files for a class, unlike Swift’s single file for a class. A primary reason for this is sharing.  Some data is shared in a class, some isn’t. We refer to how much something shares as its scope. Scope rules are very different between the two languages. One of those two files in Objective C is a header file or .h file. The .h file has information that is public in scope. A public scope allows sharing. Anything private and not shared gets declared in the other file, the  implementation file. Implementation files have  a .m extension. The .h file includes properties, methods target-actions, outlets, and often protocols for delegates. It is in the header file that you find most of the class definition. The header file has no code though: it is only declarations. You will repeat all your methods and target actions in the implementation file when you write the code for it.

In the code I found the header looks like this:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<uipickerviewdatasource,uipickerviewdelegate>
@property (weak, nonatomic) IBOutlet UIPickerView *myPicker;
@property  NSInteger maxRows;

@end

In the header file, you will find a series of tags beginning with @. You’ll find on each property a @property tag. These will be the public properties. We have one of these as an outlet to our picker view:

@property (weak, nonatomic) IBOutlet UIPickerView *myPicker;

Let’s dissect this outlet from a Swift perspective. @property (weak, nonatomic) IBOutlet sets an outlet with a weak variable. Since Swift has an even more automatic version of Automatic Reference Counting(ARC) than Objective C, we ignore the nonatomic part. Next we have the class of the identifier, UIPickerView. Finally, we have the identifier for the property *myPicker. the asterisk (*) means  this is a pointer, which is true of most object identifiers in Objective C. Pointers in Objective C can have nil values,. It’s a good idea to their Swift equivalent an optional value.

This translates in Swift to

@IBOutlet weak var myPicker: UIPickerView!

If you never saw the difference between the two languages, you might begin to see why Swift is so speedy for coding. The outlet is merely @IBOutlet. We have a weak variable so next we have weak var. Next comes the identifier myPicker with the class UIPickerView as an optional.
Swift encourages assignment of values to variables and constants at declaration. Objective C does not. In Objective C, you declare in one place and assign in another. Often the assignment happens in the viewDidLoad method of your .m file. When reading Objective C code declarations, keep the viewDidLoad handy. For example our .h file has this line:

  @property  NSInteger maxRows;

in the .m file we have this in viewDidLoad:

self.maxRows = 10;

For all properties, Objective C requires the self identifier. These two lines equal the one line in Swift.:

let maxRows = 10

Which assigns maxRows equal to an Int of 10 and sets the type to Int. Of course this also makes this a constant, which is not the case in Objective C.

There is also an @interface tag in the .h file. Our properties and any public methods are found between this tag and the @end tag.

@interface ViewController : UIViewController<uipickerviewdatasource,uipickerviewdelegate>
…
@end

The @interface tag tells us the identifier of this class, and its superclass. In this app it is UIViewController. In angle brackets after the superclass is a list of adopted protocols. In our case we adopted the picker view data source and delegate. Again Swift simplifies this to essentially one line — and a bracket

class ViewController:UIViewController,UIPickerViewDataSource,UIPickerDelegate{
}

We have two @interface tags in any Objective-C class. The second is in the .m file, and lists private properties. For example we have this:

@interface ViewController ()
    @property NSInteger maxElements;
@end

Swift simplifies this to a mere keyword: private. The viewDidload  method tells us to set this to 10,000. Given that information we get this:

 private let maxElements = 10000

Putting that all together, we can take the header file, plus  the @interface and the viewDidLoad in the implementation file to translate them into Swift:

import UIKit
class ViewController:UIViewController,UIPickerDataSource,UIPickerDelegate{
    let maxRows = 10
    private let maxElements = 10000
    @IBOutlet weak var myPicker:UIPickerView
}

The Meat of the Matter: The Implementation File

Let’s look at the implementation file now and figure out what it is doing. I’ll assume you know about UIPickerView and its delegates.  If you don’t, read this first. The Objective C file is this:

#import "ViewController.h"

@interface ViewController ()
    @property NSInteger maxElements;
@end

@implementation ViewController

#pragma mark delegates and data source

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return self.maxElements;
}

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component{
    NSString *myString;
    NSInteger myRow = row % self.maxRows;
    myString = [NSString stringWithFormat:@"%li",(long)myRow];
    return myString;
}

#pragma mark life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    self.maxElements = 10000;
    self.maxRows = 10;
    self.myPicker.delegate = self;
    self.myPicker.dataSource = self;
    [self.myPicker selectRow:self.maxElements / 2  inComponent:0 animated:NO];
}

@end

If you have never worked in Objective C before, your biggest challenge is how they put stuff backwards from Swift, C++, Java or most any other language.

Objective C bases its syntax on an older language, Smalltalk. It was a Smalltalk demonstration that inspired Steve Jobs to come up with the Macintosh in the early 1980’s. Both Smalltalk and Objective C look at objects very literally. You state what your object is and then what you want to do with it. You enclose that in square brackets. For example we have

[super viewDidLoad];

which runs the superview’s viewDidLoad. Swift writes that as a function

super.viewDidLoad()

Since this is a picker view, we need to add two data source methods to tell us about our data and one delegate method. Let’s look at one of those methods:

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}

This implements one of our two required data source methods. We have one component in our picker view, so we return 1. The minus sign at the beginning of the method declaration tells the compiler and developer that this is an instance method. If it was a plus it would be a class method. Next,  the return type is in parentheses, followed by the method identifier. If there are parameters, the parameter list follows. In Swift that looks like this:

func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}

The syntax is very different between the two, but the same information is there. The next data source method works the same, returning a value for the number of rows in the picker, which in this case is a lot.

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return self.maxElements;
}

We use maxElements to set this size, which we know is 10,000 rows. Why do we need that many rows? The answer lies in the delegate method, titleForRow:

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component{
    NSInteger myRow = row % self.maxRows;
    NSString *myString = [NSString stringWithFormat:@"%li",(long)myRow];
    return myString;
}

The declaration tells us we return a string, and have the parameters of the picker view, the row and the component. The code declares an NSInteger, called myRow which is the modulo of row and maxRows. We take this new value, convert it to a NSString, and then return the string. Unlike properties, in methods we can declare and assign at the same time. However, we need to declare the type when we do.

What does this do? The method takes a massive set of numbers and has a row in that set of 10,000. It takes the remainder of row divided by maxRows. That remainder will repeat between 0 and maxrows - 1.  In effect, the picker repeats between 0 and 9 in our case. If the number for maxElements is big enough, it is unlikely that we will run out of numbers for repetition. Since these are integers, you’ve used a small amount of memory to do this.

You can write the method like this in Swift:

    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        let myRow = row % maxRows
        let myString = String(format: "%i", myRow)
        return myString
    }

We returned the string as an optional. When converting from Objective-C to Swift, anything that is a pointer is best converted as an optional.

But we are not done yet. The Objective C code still has the viewDidload method.

- (void)viewDidLoad {
[super viewDidLoad];
self.maxElements = 10000;
self.maxRows = 10;
self.myPicker.delegate = self;
self.myPicker.dataSource = self;
[self.myPicker selectRow:self.maxElements / 2 inComponent:0 animated:NO];
}

The viewDidload method does not need to assign initial values in Swift. That happens when we declare a value. Objective C requires you to explicitly say where something is with self, while Swift is better at guessing. We can get rid of the first two assignments, and get rid of all the self references, though you can leave them there if you wish. The last line of the method makes a call to myPicker and selects a specific row in the picker: 5,000. This way if we roll up or down we will get a repeat. All this in Swift looks like this:

override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
        myPicker.selectRow(maxElements / 2, inComponent: 0, animated: false)
    }

Our Swift file looks like this now:


import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
    //MARK: Properties
    let maxRows = 10
    private let maxElements = 10000

    //MARK: Outlets
    @IBOutlet weak var myPicker: UIPickerView!
    func platypus(){
        //doesn't do much

    }
    //MARK: Delegates and DataSources
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return maxElements
    }
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        let myRow = row % maxRows
        let myString = String(format: "%i", myRow)
        return myString
    }

    //MARK: Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
        myPicker.selectRow(maxElements / 2, inComponent: 0, animated: false)
        platypus()
    }

}

If you’d like to try this code out, make a new single view project. Drag a Picker View  and a label out on to the storyboard. Make it look like this:

Screenshot 2015-03-01 13.56.14

Copy the code above into the ViewController.swift file of your project.

Using the assistant editor, control-drag from the picker to the myPicker outlet. Control-drag from the I want coffee!! label to the Assistant editor. Name the new outlet statusLabel.

Build and run:

 

Screenshot 2015-03-01 14.08.54

 

Using Text Arrays

Not everyone will want the row number. Most likely, you will want something else listed in your picker view. If you want to use text, set up an array like this:

private let coffee = ["Coffee","Latte","Mocha","Machiatto","Frappucino","Espresso","Red Eye","Black Eye","Espresso","Irish Coffee"]

Change the pickerView:titleForRow: to this.

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        let myRow = row % coffee.count
        //let myRow = row % maxRows
        //let myString = String(format: "%i", myRow)
        let myString = coffee[myRow]
        return myString
    }

We take the repeating row and use that for the index to the array. Build and run:

 

Screenshot 2015-03-01 14.09.28

 

Returning Values

To get a value from a selected wheel, we use the modulo again. This time in the didSelectRow delegate method. Add this to the code:

func pickerView(pickerView: UIPickerView,didSelectRow row: Int,inComponent component: Int){
        let myRow = row % coffee.count
        let myString = coffee[myRow]
        statusLabel.text = "I love " + myString + "s"
    }

Build and run:

Screenshot 2015-03-02 07.54.18

You can add one more line. For most simple cases it isn’t necessary if you have plenty of space to spin, but there may be times you need to limit your space, or prevent running out of space. You can add this or something like it to didSelectRow:

pickerView.selectRow((maxElements / 2) + row, inComponent: 0, animated:false)

I’d leave maxElements at 10000, and this means maxElements/2 is 5000. Since 5000 a number ending in 0,  we can position the picker back at the middle plus offset of myRow. Whenever the wheel stops, we are back a the middle. Note if maxElements/2 does not divide evenly to 0, you might run into problems and need a little more complicated math.

This is the basics of building a repeating picker view. It also illustrates some of the important issues you need to watch if converting between old Objective-C and Swift. These are not the only issues. Apple has an  e-book on the subject of using Objective-C and Swift, and it is worth the time reading.

The Whole Objective – C Code

//
//  ViewController.h
//  InfinitePicker
//
//  Created by Steven Lipton on 2/28/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<uipickerviewdatasource,uipickerviewdelegate>
@property (weak, nonatomic) IBOutlet UIPickerView *myPicker;
@property  NSInteger maxRows;

@end
//
//  ViewController.m
//  InfinitePicker
//
//  Created by Steven Lipton on 2/28/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
    @property NSInteger maxElements;
@end

@implementation ViewController

#pragma mark delegates and data source

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return self.maxElements;
}

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component{
    NSString *myString;
    NSInteger myRow = row % self.maxRows;
    myString = [NSString stringWithFormat:@"%li",(long)myRow];
    return myString;
}

#pragma mark life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    self.maxElements = 10000;
    self.maxRows = 10;
    self.myPicker.delegate = self;
    self.myPicker.dataSource = self;
    [self.myPicker selectRow:self.maxElements / 2  inComponent:0 animated:NO];
}

@end

The Whole Swift Code

//
//  ViewController.swift
//
//  Created by Steven Lipton on 2/28/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
    //MARK: Properties
    let maxRows = 100
    private let maxElements = 10000
    private let coffee = ["Coffee","Latte","Mocha","Machiatto","Frappucino","Espresso","Red Eye","Black Eye","Espresso","Irish Coffee"]

    //MARK: Outlets
    @IBOutlet weak var myPicker: UIPickerView!
    @IBOutlet weak var statusLabel: UILabel!

    //MARK: Delegates and DataSources
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return maxElements
    }
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        //let myRow = row % maxRows
        //let myString = String(format: "%i", myRow)
        let myRow = row % coffee.count
        let myString = coffee[myRow]
        return myString
    }

    func pickerView(pickerView: UIPickerView,didSelectRow row: Int,inComponent component: Int){
        let myRow = row % coffee.count
        let myString = coffee[myRow]
        statusLabel.text = "I love " + myString + "s"
        //if you want to use less elements in maxElements
        // or prevent ever running to the end of the list in your pickerview
        //you can set yourself back to the middle. It is not necessary for most cases.
        pickerView.selectRow((maxElements / 2) + row, inComponent: 0, animated:false)
    }

    //MARK: Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
        myPicker.selectRow(maxElements / 2, inComponent: 0, animated: false)
    }

}

8 Replies to “Converting Objective C to Swift : The Circular Picker View Example”

  1. do you know how to make a label for selected component? for example: coffee: mocha
    coffee: latte …. for another circle .: cold drinks: coca cola, cold drinks: orange juice
    it means to change selected value runtime in the circle for example : mocha to coffe: mocha ( only for selected value in the circle)

    1. So IN one component you have cold drinks, coffee, booze, etc, and the the other a list of lists of those drinks. you want to display that context sensitive. Right?

      You’ll need a bigger data structure and a bit more logic. You’ll need two components if I understand what you want to do. Organinze this as you wish but you need two arrays, one for each of the components. The first array is a simple string array with your drinks. the second is an array of string arrays, with each element in the second array corresponding to the first array, so element 0 may be coffee, and thus element 0 in the second array would be [coffee, latte] and so on.

      when you select a row in the first component, in the didselectrow delegate you load into the second component the array for that component, and do a reloadComponent after assigning. just off the top of my head.

      Hope that helps, if I’m not barking up the wrong tree.

  2. Hi, it is really useful but I have a doubt, I would like to use the pickerview with the number, but I would like to extort the number selected to use in a calculation formula. I would like to do it throw a button but I’m not able to do it, I obtain the number which is part of the repetition in the spin. Could you help with it?

    Regards and happy Xmas

  3. Hi, it is really useful but I have a doubt, I would like to use the pickerview with the number, but I would like to extort the number selected to use in a calculation formula. I would like to do it throw a button but I’m not able to do it, I obtain the number which is part of the repetition in the spin. Could you help with it?

    Regards and happy Xmas

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s