Getting array objects into NSTableView - arrays

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

Related

How to order PFObjects based on its creation date?

I have some user comments stored in a database (parse-server) that I would like to would like to display on my viewController's viewDidLoad(). I can easily pull the comment objects as follows:
super.viewDidLoad()
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.whereKey("objectId", equalTo: detailDisclosureKey)
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count == 1{
for object in objects{
self.unOrderedComments.append(object)
}
}
}
}
}
This query dumps all of the of the comments in the unOrederedComments array. Each comment is added to the database with a createdAt property automatically being added relating the exact time of its creation. This property is a string with (as an example) the form: "2017-08-13T19:31:47.776Z" (the Z at the end is at the end of every string... not exactly sure why its there but its constant). Now, each new comment is added in order to the top of database and thus any queried result should be in order regardless. However, I would like to make sure of this by reordering it if necessary. My general thought process is to use .sorted, but I cannot figure out how to apply this to my situation
func orderComments(unOrderComments: [PFObject]) -> [PFObject]{
let orderedEventComments = unOrderedEventComments.sorted(by: { (<#PFObject#>, <#PFObject#>) -> Bool in
//code
})
}
This is the generic set up but I cannot, despite looking up several examples online figure out what to put in the <#PFObject#>'s and in the //code. I want to order them based on the "createdAt" property but this is not achieved via dot notation and instead requires PFObject["createdAt"] and using this notation keeps leading to error. I feel as so though I may need to set up a custom predicate but I do not know how to do this.
I was in the same situation, what I did was to first create an array of structs with the data I downloaded where I turned the string createdAt into a Date, then used this function:
dataArrayOrdered = unOrderedArray.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
(.date being the stored Date inside my array of strcuts)
Try this code, notice that I assumed you have a variable name called ["Comments"] inside your Parse database, so replace if necessary. Also, I realised that createdAt it's in Date format, so there was no need to change it from String to Date, chek if it works the same for you, if it doesn't refer to this: Swift convert string to date.
struct Comment {
var date = Date()
var comment = String()
}
var unOrderedComments: [Comment] = []
var orderedComments = [Comment]()
override func viewDidLoad() {
super.viewDidLoad()
query()
}
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count >= 1{
for object in objects{
let newElement = Comment(date: object.createdAt!, comment: object["Comments"] as! String)
self.unOrderedComments.append(newElement)
print(self.unOrderedComments)
}
}
self.orderedComments = self.unOrderedComments.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
print(self.orderedComments)
}
}
}

Error after update to Xcode 7

I have browsed through some of the other posts relating to upgrade and errors but I did not see one that I thought answered my issue. The following code was working yesterday but today after upgrading, I am getting an error:
enteredDataArrayOne = [enterDate.text, enterSeason.text, enterSport.text, enterDispTo.text]
The error is as follows:
Cannot assign a value of type '[String?]' to a value of type 'NSArray'
I am very new to coding. I have gone back to review my instructional materials and it is my understanding that building an array of strings is allowed. The values that make up the array enteredDataArrayOne are the values of text fields entered by the user.
Here are the other two places that this array is used:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "goToDispenseScreenTwo") {
let DestViewControllerTwo = segue.destinationViewController as! DispenseScreenTwoViewController;
DestViewControllerTwo.toPassTwo = enteredDataArrayOne as! [String]
}
}
and then later in the same method where the array is built:
if blankData != 1 {
//add code to pass data to next veiw controller
enteredDataArrayOne = [enterDate.text, enterSeason.text, enterSport.text, enterDispTo.text]
print(enteredDataArrayOne)
self.appIsWorking ()
performSegueWithIdentifier("goToDispenseScreenTwo", sender: self)
activityIndicator.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
I appreciate any suggestions.
This should fix your error
enteredDataArrayOne = [enterDate.text!, enterSeason.text!, enterSport.text!, enterDispTo.text!]

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

variable of object type as an Array misbehaving

environment XCode 6.4 Swift 2.something
I have two simple Custom Classes
class Stock {
var ageArray = [3,6,9,12,15,18,24,30,36,42,48,54,60,66,72] // in months
var beastArray = ["Angus","Braford","Charolais","Droughtmaster","Euro","Hereford"]
var breedArray = ["Calves","Vealers","Weaners","Heifers","Steers","Cows","Bulls"]
var priceArray = [600.00,650.00,700.00,750.00,800.00,850.00,900.00,950.00,1000.00,]
}
and
class Sale {
var age : Int = 0
var beast : String = " "
var breed : String = " "
var price : Double = 0.00
}
Now to my view controller
I've instantiated a report from Sale Class. The ViewController holds an array of all those reports as I'm taught in nuremous places, declaring it first and instatiating it in viewDidLoad
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
var report = Sale()
var sheets = Stock()
var saleArray : [Sale] = [] // declaration
override func viewDidLoad() {
super.viewDidLoad()
saleArray = [Sale]() // instantiating
}
the other delegate functions of the picker work sweetly, no need to show them all here, but this is where the two class Objects meet to pass values
func pickerView(picker: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch(component) {
case 0:
report.breed = sheets.breedArray[row]
break
case 1:
report.beast = sheets.beastArray[row]
break
case 2:
report.age = sheets.ageArray[row]
break
default:
report.price = sheets.priceArray[row]
break
}
}
and an onscreeen button loads them into an array
#IBAction func AcceptValues(sender:UIButton) {
saleArray.append(report)
}
that's all there is to it.
This is the result as seen in the debugger with a breakpoint set as below after 3 times around with [0] supposed to have Angus Calves and [1] having Droughtmaster Heifers
so the question is why would the saleArray be filling up with n instances of whatever was last entered? I know i'm not looking at the last entry n times. But curiously the address for report is all the same at 7A19C000.
I thought it might be the compiler can't infer the array type after the instatiaton in viewDidLoad. Examining various tutorials and the code supplied with them treats the matter more or less as I have above. Which is the reason for the title, I'm sure Swift can hold an array of objects other than the usuals.
I've even gone to the expense at purchasing not one, but two of Matt Neuberg's books just to check on how to set up an Object Array, but like all the material available they just discuss arrays of Int and Double and String and no more. Sometimes the most infuriating bugs are the most staring-at-you-in-your-face, and I've been chasing this for close on a week.
There's little else to it the code.
EDIT:
thanks to the two contributors below, the comments show what i did to make it work. But I have a feeling its not elegant:
I also made Stock a struct.
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
#IBOutlet weak var textPanel: UITextView!
var sheets = Stock()
var saleArray : [Sale] = []
var localBreed = " " // introduced some handover values
var localBeast = " " // because report is out of scope
var localAge = 0 // in the picker functions
var localPrice = 0.00
.
.
.
#IBAction func AcceptValues(sender:UIButton) {
var report = Sale() // new reports instantiated here
report.breed = localBreed // and locals handover values to
report.beast = localBeast // report instance before adding
report.age = localAge // to array
report.price = localPrice
saleArray.append(report)
}
.
.
.
func pickerView(picker: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch(component) {
case 0:
localBreed = sheets.breedArray[row]
break
case 1:
localBeast = sheets.beastArray[row]
break
case 2:
localAge = sheets.ageArray[row]
break
default:
localPrice = sheets.priceArray[row]
break
}
}
I believe that it is because report is a single instance of Sale. Therefore each time you select a new combination - you are not creating a new Sale item, rather you are changing the values associated with that single instance of Sale called report. When you press the button it adds another reference to that single report instance.
These class objects are passed by reference - i.e. when you add report to the saleArray it is not creating a copy of the values - rather you are passing a reference to the actual instance. Each time you press the button you are adding a reference to the same instance of Sale. And the values associated with that instance are then getting changed when you use the picker. It's the same instance over and over.
You would need to change your code so that you create a new instance of Sale for each selection. As a quick hack, to test this, you could add code to your button which creates a new Sale instance based on the selected values - and then adds that to saleArray.
ETA - one other thing I noticed: You are instantiating saleArray twice - both at //declaration and in viewDidLoad(). Although that is not causing the issue you have described, it could cause an issue under potential circumstances.

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

Resources