NSIndexPath in Swift - arrays

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")

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.

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
}

Related

Passing a Model between View Controllers

I am struggling to pass a model of data from one controller to the next. I am sure I am just missing something very simple but hoping I can get some help.
Below I create the code to select all the data from the row that was selected:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print(indexPath.row)
let passingData = arrivals[indexPath.row]
print(passingData)
let controller = storyboard?.instantiateViewController(withIdentifier: "FlightInfoVC") as! FlightInfoViewController
controller.flightDataPassedIn = passingData
//Code incomplete - Will add execution to show VC
}
That print statement is reflected below:
Now where I am struggling is to get that data to go to my next view controller. Setting the array of data in my new VC doesn't seem to work because it is expecting a type of 'FlightModel' but I cannot seem to figure out how to declare that. I have tried this but I cannot convert type [Any] to 'FlightModel'
var flightDataPassedIn: FlightModel = []
Appreciate any help you can give!
It sounds like the issue is that you're trying to initialize flightDataPassedIn with a value of [], which is of incompatible type [Any].
If you're okay with the flightDataPassedIn property on FlightInfoViewController being optional, that might be the most straightforward solution. It would automatically be initialized to nil, so would not require you to set an initial value - just declare it like this:
var flightDataPassedIn: FlightModel?
If you don't want it to be optional, initialize it to a default value of type FlightModel (add an initializer to FlightModel if necessary) and overwrite it with passingData from the parent ViewController.
Hope this helps!
The issue was that I was not passing the data via the main required function that everyone uses Prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TrackingToInfo" {
if let destinationVC = segue.destination as? FlightInfoViewController {
destinationVC.flightDataPassedIn = readyToPass
}
}
}
Within the tableview I had to set the data to a var in my currentVC of type FlightModel? then the data passed

An array of user id's that you want to display information of; how do you filter for those elsewhere in the code?

Say you have a subset of users in an array via their uid. You want to display the information of these users in your application. So how do you use this array elsewhere in the code to only display for those uids.
There are 2 pages where I have to do this, and on one I managed to fix it with if self.array1.contains(people.key) However on the second page (I assume because it is structured differently), that does not work.
//Part 1: The array and current attempt
var array1 = [String]() ///this is at top of code before viewDidLoad
///In view did load
if let unwrappedName = name {
self.array1.append(unwrappedName)
}
}
print(self.array1, "ahah")
///Also in view did load, in a dataSnapshot, above the array definition
refArtists = Database.database().reference().child("people");
refArtists.observe(DataEventType.value, with: {snapshot in
for people in snapshot.children.allObjects as! [DataSnapshot] {
if people.key != thisUsersUid {
**if Calendar.current.isDateInToday(date) && countb >= 1 && self.array1.contains(people.key) {**
}
...
self.people.append(peopl)
...
self.people.sort(by: { $0.TodayLikeCount ?? 0 > $1.TodayLikeCount ?? 0 })
print("aaaaaaaa", self.people.map {$0.TodayLikeCount})
self.table.reloadData()
///How is this page structured:
1)class ranking: UITableViewController, CLLocationManagerDelegate{
2) All the definitions like var = this, let this = that etc
3)public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
4)public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell ///Return cell is made here
5)override func viewDidLoad(), super.viewDidLoad(), table.dataSource = self, table.delegate = self
6) **The refArtist.obeseve snapshot posted above**
7)**The defining of the array**
8)Other code
For filtering an array and show it, you have to create another array and store the sorted results in that array, and use that array to reload the tableView. Also initially keep that array same as peopleArray by default. Then you can pass around this array to other pages as well.
var filteredArray = [String]() // declare at the top
Inside the viewDidLoad
filteredArray = self.people.sort(by: { $0.TodayLikeCount ?? 0 > $1.TodayLikeCount ?? 0 })
self.table.reloadData()
Hope you are clear.

How to effectively load the properties string for all rows of data from realm into UILabels in a UITableView

Within my realm I have RealmClassA, with many rows of information that the user can successfully add to whilst running in simulator.
I am trying to read the data from realm (technical term Query?!) to display some of the data into a TableViewController within a customCell that houses many UILabels. The customCell is of type TableViewCell.
For examples sake I have included only some of the properties and labels.
I would like to display the data from propertyA column, into the UIlabel's in alphabetical order, alongside the other data in propertyB column for that row entry. See image below.
The TableViewController is;
import UIKit
import RealmSwift
class TableViewController: UITableViewController {
let realm = try! Realm()
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
//After solving this question, this numberOfSections return will return the count of the number of unique strings that the propertyB column that RealmClassA houses.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return realm.objects(RealmClassA.self).count
// this DOES return a number of cells that matches the number of entries (rows) in RealmClassA.
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCellID", for: indexPath)
// this is where I have hit a brick wall and guidance is needed.
return cell
}
}
The customCell is of type TableViewCell;
import UIKit
import RealmSwift
class TableViewCell: UITableViewCell {
#IBOutlet weak var propertyAGoesHere: UILabel!
#IBOutlet weak var propertyBGoesHere: UILabel!
#IBOutlet weak var propertyCGoesHere: UILabel!
//etc.
let realm = try! Realm()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I am able to count the number of entries in the Output using the following;
print("The number of RealmClassA records in memory is",realm.objects(RealmClassA.self).count)
It prints out The number of RealmClassA in memory is 10
When I attempt to print just the names of the stings in propertyA column;
let allEntriesArray = allEntriesList.map{$0.PropertyA}
print("Querying all propertyA entries in realm \(allEntriesArray)")
it prints out
Querying all propertyA entries in realm LazyMapSequence<Results<RealmClassA>, String>(_base: Results<RealmClassA> <0x7fd2f8cb4f20> (
[0] RealmClassA {
propertyA = Random Words for Filler
propertyB = More Random words to fill the property information;
//etc.
},
// this repeats for all entries in the RealmClassA.
Help needed with
HOW to effectively access the data from realm to display in the table view. I'm clearly nearly there, as I can display the correct number of cells for the number of entries in RealmClassA.
Whether I have over complicated something along the line.
I have used the following sources as help to no avail.
https://www.youtube.com/watch?v=0WOd6GVPMb0
UITableView with Multiple Sections using Realm and Swift
Querying Realm to populate numberOfRowsInSection and cellForRowAt with Multiple Sections using Realm and Swift
How to access properties of an object returned from primary key query in Realm Swift
And I have scoured the Realm.io documentation here
https://academy.realm.io/posts/realm-list-new-superpowers-array-primitives/
https://realm.io/docs/swift/latest/#getting-started in the List section
https://realm.io/docs/swift/latest/api/Classes/List.html
update
Changing any instances of ‘PropertyA’ to ‘propertyA’ throughout.
Main question for clarity.
How to display all data from one column in saved in realm, in an alphabetical order inside a tableview cells’ UILabels.
Apologies for the length of post, I see a lot of questions commented with people asking for all the information so I have done my utmost!
update 2
Removing unnecessary information for clarity after answer posted
Thanks to #Jay's help plus this website;
https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/custom-cells/,
I found I was missing as! customCell from the end of the cellForRowAt func.
Here is how it should look if anyone is looking at this further down the line.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCellID", for: indexPath) as! customCell
let itemsToDisplay = realm.objects(RealmClassA.self).sorted(byKeyPath: "propertyA")[indexPath.row]
cell.propertyADisaplyLabel?.text = itemsToDisplay.propertyA
cell.propertyBDisplayLabel?.text = itemsToDisplay.propertyB
cell.propertyCDisplayLabel?.text = itemsToDisplay.propertyC
return cell
}
EDIT
Setting up a tableView dataSource on macOS - iOS is very similar. This sets up a dataSource with 10 objects. This example is really to show a proper use of the dataSource.
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
var dataArray = [String]()
#IBOutlet weak var myTableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...9 {
let s = "row_\(i)"
self.dataArray.append(s)
}
self.myTableView.delegate = self
self.myTableView.dataSource = self
self.myTableView.reloadData()
}
func numberOfRows(in tableView: NSTableView) -> Int {
return self.dataArray.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let identifier = NSUserInterfaceItemIdentifier("MyCellView")
guard let cell = tableView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView else {return nil}
cell.textField?.stringValue = self.dataArray[row]
}
Note the NSTableCellView (Table Cell View) identifier is set to MyCellView.

Getting array objects into NSTableView

I want to catch information from an array and display it into my NSTableView. I'm unsure what I need to do for that (I am very new to Swift and programming in general).
My table view looks like this:
I want to get value name from my array and display it in the name table with the NSTableView. I've found this tutorial on Ray Wenderlich but the code is very outdated and I don't want to use old things within my project which might not work anymore in newer OS versions.
It seems that I need an [NSTableViewDataSource numberOfRows][3] and viewFor.
Any examples on how to do this - maybe someone made this a few weeks ago with Swift 3? :D
The information within the array will be generated by the following:
var devices = [Device]()
let quantityDevices = quantityData.intValue
for i in 0...quantityDevices-1 {
let newDevice = Device()
print("created new device")
newDevice.name = titleData.stringValue + "-\(i)"
devices.append(newDevice)
}
print("now we have \(devices.count) devices in our array")
}
The important part of the code you need is the DataSource delegate functions:
extension ViewController : NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return devices.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
// 1 - get the device for this row
guard let device = devices[row] else {
return nil
}
// 2 - configure the cell with the device data
return nil
}
There is an example here on StackOverflow that should give a better example

Fetch Parse Data based on Array

I have a PFQueryTableViewController.
I also have an array of numbers in a randomly generated order, from 1 to the Parse class count.
In Parse, each object in the class is assigned a number (1,2 ,3, etc.)
I want to fetch objects from this class in Parse to table view cells,and the order depends on the order of the array.
For example, my array would be [3, 2, 5, 1, 4]
I want to query where key "number" is equal to those numbers, in that order..
So far I have this:
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery{
var query = PFQuery(className: "Stores")
for i in 1...10 {
query.whereKey("number", equalTo: numArray[i])
}
return query
}
and
//override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as! ExploreCell!
if cell == nil {
cell = ExploreCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
however, whenever i run, it only grabs information from 1 objects. My table view only has 1 cell that grabbed data.
I suppose that's Swift Code (I actually don't know Swift).
Anyway I don't see the need to (try to) query the objects in the order you want from array. Actually using query.whereKey more times you will just use the last one.
What you should do, is actually query the objects and then sort them (unless you want an ascending/descending order).
Also keep in mind that a Parse Query is asynchronous so probably structuring the function like you did won't work like you want.
EDIT: This is the kind of code you may want to use:
var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
The objects variable is the array you want to sort.
The Parse Documentation has been always useful to me to actually understand better Parse

Resources