How to populate a NSTableView on a button click in Swift - macos

I have got a CellBased NSTableView. I need to populate it on Button Click. I have written the code for populating the table implementing NSTableViewDataSource protocol. I have used an Array as Data Source.
The code is as below
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
let numberOfRows:Int = dataArray.count
return numberOfRows
}
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
}
But everything is getting loaded when I start the application. I want the data to get loaded on a Button Click like an IBACtion
#IBAction func findButton(sender: AnyObject) {
}
Please help!

Don't set the delegate via interface builder, or elsewhere, set it in your button:
#IBAction func findButton(sender: AnyObject) {
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
}

Try setting your UITableView.dataSource to nil. Then when you are ready (button click) call tableView.dataSource = self. You might also have to call tableView.reloadData().
Haven't tested this but should in theory work.

Related

NSButton state issues

I have two ViewControllers.
On VC1 I have search criteria and on VC2 I have the search results. If you want to go from VC2 to VC1 the VC2 is dismissed.
On VC1 I have an NSButton(style Check, type Switch) which by default I want it to be in ON state. The purpose of the NSButton is to include photos in the results.
If the user unchecks the button and presses search, it will go on to VC2 showing the search results without photos.
BUT when the user goes back to VC1 for a new search that's where the unwanted behaviour occurs:
The NSButton is unchecked(i want it to be checked by default, every time the user is at the VC1. Also, the button is nil.
Why is this happening, and how can i make it the button box to be ticked everytime the VC2 is dismissed?
I tried enabling it and setting it to ONState but as its nil it would crash.
To set a state every time your controller opens use the method
-(void)viewWillAppear
To let viewControllers communicate with each other you can implement delegates. Here is a pretty good tutorial: Link
Another approach is to communicate with Notifications -> Link
Or you can set values on methods like prepareForSegue - depending on what you use to imstantinate your controllers.
I have managed to make it perform the way I want it by adding
switch.state=1
just before the segue from VC1 to VC2 is performed.
However, I don't think this is the most elegant solution as the button is still nil.
UPDATE:
I have figured out that the issue occurs as when it goes from VC1 to VC2 the VC1 becomes nil, when the VC2 is dismissed it becomes nil as well. Hence the crash. One solution is to use delegates.
VC1:
class FirstViewController: UIViewController,SecondViewControllerProtocol {
#IBOutlet var firstName: UITextField!
#IBOutlet var lastName: UITextField!
#IBOutlet var subscribeSwitch: UISwitch!
#IBAction func goToSecondVC(_ sender: Any) {
let viewController = self.storyboard?.instantiateViewController(withIdentifier: String(describing: SecondViewController.self)) as! SecondViewController
viewController.delegate = self
self.present(viewController, animated: true, completion: nil)
}
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.
}
func dismissViewController() {
if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController"){
subscribeSwitch.isOn=true
}
}
}
VC2:
protocol SecondViewControllerProtocol {
func dismissViewController()
}
class SecondViewController: UIViewController {
var delegate:SecondViewControllerProtocol!
#IBAction func goBackToFirstVC(_ sender: Any) {
self.dismiss(animated: true) {
self.delegate!.dismissViewController()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

How to Pass Collection View Data to a New View Controller

I am going try be as specific as possible.
What I am trying to achieve is to have one view controller which has a collection view in it and when the user clicks on of the collection view cells, it then sends the user to another view controller which has another collection view however the items displayed in the second collection view will change depending on which collection view cell the user previously tapped. I am also using CoreData to do this. With CoreData, the task is an entity and has attributes like name etc. Will I have to change this or have a relationship between the 2 collection views?
The reason for me wanting to do this is because I am creating a productivity app for iOS and the first view controller with a collection view will be where the user can create projects, within these projects which will be displayed in a collection view, the user can then tap on one of the cells and go to the next view controller and begin to create tasks specific to that project.
How can I keep the tasks stored in a specific collection view cell and have the user create different tasks in other projects. It is sort of like Wunderlist. If anybody is confused at what I am trying to do, I can ellaborate more.
This is one of my view controllers where the data from the 'create item view controller' gets sent to via CoreData and displays it in a collection view, hope it helps:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var myCollView: UICollectionView!
#IBAction func addCore(_ sender: UIButton) {
}
var tasks : [Task] = []
override func viewDidLoad() {
super.viewDidLoad()
self.myCollView.delegate = self
self.myCollView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getData()
myCollView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tasks.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "connectCell", for: indexPath) as! MyCollectionViewCell
let task = tasks[indexPath.row]
cell.labelTe?.text = task.name!
self.myCollView.backgroundColor = UIColor.clear
cell.layer.cornerRadius = 25
return cell
}
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
tasks = try context.fetch(Task.fetchRequest())
}
catch {
print("Ahhhhhhhh")
}
}
If I am guessing right, and upon clicking a cell on the first collection view, you want to segue to another collection view, where you will display display data according to the selected cell.
In order to achieve this, simply add collectionView(_ collectionView:, didSelectItemAt indexPath:) and prepare(for segue:, sender:) functions in your first view controller.
In the collectionView(_ collectionView:, didSelectItemAt indexPath:) function get the id of the task selected by determine which cell was selected, then pass this id to the second view controller in prepare(for segue:, sender:) function.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! MyCollectionViewCell
id = cell.labelTe?.text
}
or
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
id = tasks[indexPath.row].id
}
Then pass this value in your prepare(for segue:, sender:) function to the second viewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "YourIdentifier"{
let destinationViewController = segue.destination as! SecondViewController
destinationViewController.id = id
}
}
Make sure to add a var id in both your viewControllers. In the second viewController, use this id to fetch the data of the selected task that you want to display.
Hope this helps.
EDIT
Simply declare a new variable id of your particular type in both the viewControllers. In your first viewController :
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var id = ""
and in the second viewController
class secondViewController: UIViewController {
var id = ""
Just make sure that both the variable are of same type.

What is the right syntax for tableView functions in Swift 3? xCode 8 issues

I have just upgraded to xCode Version 8.0 (8A218a). I have been trying to convert some older code to Swift 3.0 and I am struggling to understand how to fix some issues with a tableView and I am confused by the documentation and some errors I am getting.
I am getting this error
*** Illegal NSTableView data source (). Must implement numberOfRowsInTableView: and
tableView:objectValueForTableColumn:row:
When running this code:
class listMusicEvents: ViewController, performanceDataModelDelegate, NSTableViewDelegate, NSTableViewDataSource {
let eventModel = performanceDataModel.sharedInstance
var musicEvents:[performanceEvent] = []
#IBOutlet var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
eventModel.delegate = self
tableView.delegate = self
eventModel.getPerformanceEvents()
}
func performanceEventsLoaded() {
musicEvents = eventModel.eventList
print(musicEvents[0].eventTitle)
}
func performanceSaveError(headline: String, message: String, error: NSError) {
print(error)
}
func numberOfRowsInTableView( in tableView: NSTableView) -> Int {
return musicEvents.count
}
func tableView(_tableView:NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> AnyObject{
if tableColumn?.title == "Date" {
let date = musicEvents[row].eventDate
return date as AnyObject
} else {
let title = musicEvents[row].eventTitle
return title as AnyObject
}
}
}
I thought I had implemented the new methods, but it looks like I am not understanding something basic.
I am getting a warning on the objectValueFor function:
Instance method 'tableView(tableView:objectValueForRow:row:)' nearly
matches optional requirement 'tableView(:setObjectValue:for:row:) of
protocol 'NSTableViewDataSource'
I've looked at the delegate docs and they say setObjectValue should not be used for a view-based tableView, which is what I have (I think).
The code I was using originally for the table column was:
tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? { . . .
This line worked fine before brining the project into xCode 8
Can someone please explain how view-based tableViews are supposed to work with Swift 3 and what I am getting wrong? Any help very much appreciated.
Restarting my computer eliminated the Illegal NSTableView data source. I guess xCode 8 worked itself into a state where it could not let go of the error.
The objectValueFor function provided by auto complete was the correct one.
I solved this problem as follows.
Add NSTableViewDataSource protocol to the ViewController class.
class ViewController: NSViewController,NSTableViewDataSource{
// snip //
func numberOfRows(in tableView: NSTableView) -> Int {
// snip //
return count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any?{
// snip //
return element
}
}
Update for Swift 3
I had to use the following prototypes...auto-complete is your friend:
func numberOfRows(in tableView: NSTableView) -> Int
and
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?

Xcode 7 - Cell content not displayed into an empty label

I'm trying to display a cell content into an empty UILabel, but after I upgraded to Xcode 7, the content didn't show up. I was following the same mechanism and it worked on Xcode 6, but since I'm new to Swift I may have something wrong in the code.
TableViewController:
import UIKit
class TableViewController: UITableViewController {
struct dataModel {
var name:String
var val:Double
}
let foods = [dataModel(name: "name1", val: 3.3),
dataModel(name: "name2", val: 5.5)]
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return foods.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
// Configure the cell...
let foodCell = foods[indexPath.row]
cell.textLabel?.text = foodCell.name
return cell
}
var valueToPass:String!
var valueInt:Int!
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel!.text
performSegueWithIdentifier("showCalc", sender: self)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showCalc") {
let viewController = segue.destinationViewController as! calcViewController
viewController.passedValue = valueToPass
}
}
}
ViewController where I want to display the tapped cell into an empty label:
import UIKit
class calcViewController: UIViewController {
#IBOutlet var text: UILabel!
#IBOutlet var empty1: UILabel!
var passedValue:String!
override func viewWillAppear(animated: Bool) {
empty1.text = passedValue
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
As a result, there is no error, but there is nothing displayed inside the empty label.
The other thing I want to achieve is to display the Int value into another label and then do some calculations, but this would be another question. Thanks in advance!
There are several issues in this code. First you are obviously missing some closing braces. But I assume this is a copy-paste error.
Why do you use didDeselectRowAtIndexPath? Don't you have a segue from the table view cell to the next controller? Then you should get the text label in prepareForSegue by using tableView.indexPathForSelectedRow.
If you like to keep it this way you should use didSelect... in stead of didDeselect....
You should take the value you want to pass to the next view controller direct from the data and not from the cell. You have the data you present in the table in the foods array.
Class names should always start with a capital letter. ALWAYS!
Having the data model within the controller is poor design. You should have the struct separated in its own file. In this case you could even hand the complete data object to the next view controller.

Save UITableView names to array on button action

I want to save the names that are filled in UITableView to an array
var mineSpillere = [String]()
How can I do this on buttonAction?
This is button action for how i add names to tableview:
#IBAction func addButtonAction(sender: AnyObject) {
mineSpillere.append(namesTextBox.text)
myTableView.reloadData()
}
and also here code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.myTableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
cell.textLabel!.text = self.mineSpillere[indexPath.row]
return cell;
}
That only when I press the "back" button in the application, the names from the UITableView will be saved in an array.
I am going to access these names from another view controller also, so i need them to be saved as an array.
Okay so it looks like you have all the data in the mineSpillere array already as you're using cell.textLabel!.text = self.mineSpillere[indexPath.row] so to get that array back to the first view controller. You could use the following code in the view controller that is being presented when the 'back' button is being pressed:
class FirstViewController: UIViewController,SecondViewControllerDelegate {
var mineSpillereCopyFromSecondVC = [String]()
func presentSecondViewController() {
// Im not sure the class name for you're second view
// where the tableView is located.
var secondVC: SecondViewController = SecondViewController();
secondVC.delegate = self
// Also not too sure on how you're presenting the `SecondViewController` so I'm goingg to leave that empty.
// The important part is just above us.
}
func passMineSpillere(mineSpillere: [String]) {
// This class now has the mineSpiller array of name.
self.mineSpillereCopyFromSecondVC = mineSpillere
}
}
And then in you SecondViewController where the tableView is located you could add the following code.
import UIKit
protocol SecondViewControllerDelegate {
func passMineSpillere(mineSpillere:[String])
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
var mineSpillere = [String]()
// This will be called when the 'back' button is pressed.
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// Pass the mineSpillere array back to the delegate
// which is the firstViewController instance.
self.delegate?.passMineSpillere(self.mineSpillere)
}
}

Resources