Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSIndexPath in Swift

I know, hopefully, that class NSIndexPath deals with arrays which has arrays.

I have this code:

import UIKit

class ViewController: UIViewController, UITableViewDataSource {

    let devCourses = [
        ("iOS App Dev with Swift Essential Training","Simon Allardice"),
        ("iOS 8 SDK New Features","Lee Brimelow"),
        ("Data Visualization with D3.js","Ray Villalobos"),
        ("Swift Essential Training","Simon Allardice"),
        ("Up and Running with AngularJS","Ray Villalobos"),
        ("MySQL Essential Training","Bill Weinman"),
        ("Building Adaptive Android Apps with Fragments","David Gassner"),
        ("Advanced Unity 3D Game Programming","Michael House"),
        ("Up and Running with Ubuntu Desktop Linux","Scott Simpson"),
        ("Up and Running with C","Dan Gookin") ]

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return devCourses.count
    }

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

        var cell = UITableViewCell()

        let (courseTitle, courseAuthor) = devCourses[indexPath.row]
        cell.textLabel?.text = courseTitle

        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

What I don't understand how NSIndexPath works. I have read documentation but is it still too hard for me to understand at this point of my learning how this works. How can I know NSIndexPath has property of row? Cans somebody explain every part of this line please:

let (courseTitle, courseAuthor) = devCourses[indexPath.row]

How does NSIndexPath know that courseTitle in this constant refers to index number 0 whitin array index 0 ("iOS App Dev with Swift Essential Training","Simon Allardice")

like image 673
Antonija Avatar asked Jul 05 '15 11:07

Antonija


People also ask

What is NSIndexPath in Swift?

Swift version: 5.6. Index paths describe an item's position inside a table view or collection view, storing both its section and its position inside that section.

How does Swift compare to IndexPath?

The Swift overlay to the Foundation framework provides the IndexPath structure, which bridges to the NSIndexPath class. This means that, as an alternative to NSIndexPath , starting with Swift 3 and Xcode 8, you can use IndexPath . Note that IndexPath also conforms to Equatable protocol. Therefore, you can use == or !=

How do I create an IndexPath in Swift?

To create an IndexPath in objective C we can use. NSIndexPath *myIP = [NSIndexPath indexPathForRow: 5 inSection: 2] ; To create an IndexPath in Swift we can use.


2 Answers

What I don't understand how NSIndexPath works.

For iOS, you can think of NSIndexPath as a read-only structure that contains two Int properties section and row if you're working with UITableViews or section and item if you're working with UICollectionViews.

You create them with the NSIndexPath:forRow:inSection: factory method:

let indexPath = NSIndexPath(forRow: 1, inSection: 0)

and read them by accessing their properties:

print("row is \(indexPath.row)")

How can I know NSIndexPath has property of row?

Xcode has some nice features to help you understand the code you are looking at. In Xcode, Option-click on row in the above statement line, and it will tell you what it is. In the pop-up, if you click on NSIndexPath UIKit Additions next to Reference, it will bring up the documentation.

Can somebody explain every part of this line please:

let (courseTitle, courseAuthor) = devCourses[indexPath.row]

devCourses is an array of tuples containing two values of type String. You can see this by option-clicking on devCourses and it shows [(String, String)]. Had it been an array of array of String it would have said [[String]] or [Array<String>] (which is two ways of saying the same thing).

indexPath.row is just an Int indicating the selected row of the tableView.

devCourses[indexPath.row] then is the tuple at that index. For example, if the row were 0, then the tuple would be ("iOS App Dev with Swift Essential Training","Simon Allardice").

Finally, you can initialize two variables simultaneously by declaring them as a tuple and initializing them with a tuple. For example:

let (a, b) = (3, 4)

creates two Int constants a and b and assigns 3 to a and 4 to b.

So this statement is retrieving the tuple from the array indicated by the integer indexPath.row and creating two variables, assigning the first variable courseTitle the value of the first value in the tuple and assigning the second variable courseAuthor the value of the second value in the tuple.


Update for Swift 3

NSIndexPath still exists in Foundation, but when working with indexPaths in Swift 3, a new type IndexPath is now used. This type is a structure.

You can still create an NSIndexPath in Swift 3, with the following updated syntax, but you can't change the properties:

var ip = NSIndexPath(row: 0, section: 0)

ip.row = 5    // Cannot assign to property: 'row' is a get-only property

but you should use the new IndexPath structure instead.

IndexPath is created with a similar syntax:

var ip2 = IndexPath(row: 0, section: 0)

ip2.row = 5   // this works

You can convert between IndexPath and NSIndexPath by casting with as:

let ip = NSIndexPath(row: 0, section: 0)
let ip2 = ip as IndexPath     // convert to IndexPath
let ip3 = ip2 as NSIndexPath  // convert back to NSIndexPath

All of the Cocoa and Cocoa Touch API's that use indexPaths have been converted to use IndexPath in Swift.

like image 154
vacawama Avatar answered Oct 26 '22 04:10

vacawama


Slightly off topic, but I'd like to encourage everybody to use custom classes for the model rather than "primitive" types. There's a little effort but big benefit.

Simple class with two properties and a description (the description variable is helpful while debugging)

class DevCourse : Printable {

  var author : String
  var title : String

  init(author : String, title : String) {
    self.author = author
    self.title = title
  }

  var description : String { return "DevCourse \(title) by \(author)"}
}

The devCourses array can be easily mapped to create the DevCourse instances with one line

  let rawDevCourses = [
    ("iOS App Dev with Swift Essential Training","Simon Allardice"),
    ("iOS 8 SDK New Features","Lee Brimelow"),
    ("Data Visualization with D3.js","Ray Villalobos"),
    ("Swift Essential Training","Simon Allardice"),
    ("Up and Running with AngularJS","Ray Villalobos"),
    ("MySQL Essential Training","Bill Weinman"),
    ("Building Adaptive Android Apps with Fragments","David Gassner"),
    ("Advanced Unity 3D Game Programming","Michael House"),
    ("Up and Running with Ubuntu Desktop Linux","Scott Simpson"),
    ("Up and Running with C","Dan Gookin") ]

let devCourses = rawDevCourses.map { DevCourse(author: $0.1, title: $0.0) }

the lines to apply the properties look much clearer

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

    let cell = tableView.dequeueReusableCellWithIdentifier("MyIdentifier", forIndexPath: indexPath) as! UITableViewCell

    let course = devCourses[indexPath.row]
    cell.textLabel?.text = course.title

    return cell
}
like image 25
vadian Avatar answered Oct 26 '22 03:10

vadian