How to sort NSTableView with NSArrayController programmatically? - swift

I have a NSTableView with just one column but different values in subviews. The tableView is populated by NSArrayController which is bound to a CoreData entity.
I was wondering how to sort the tableView by custom buttons.
class ViewController: NSViewController {
#IBOutlet var arrayController: NSArrayController!
#IBAction func sortByName(_ sender: Any) {
arrayController.sort... ?
}
#IBAction func sortByAge(_ sender: Any) {
arrayController.sort... ?
}
}
I know how to make NSArrayController to sort the columns by clicking on the row header. But since I have only one column but different values, I´m trying to find a way to sort by custom buttons.
Any help would be much appreciated.

Set the sortDescriptors property of the array controller and call arrayController.rearrangeObjects()
class ViewController: NSViewController {
#IBOutlet var arrayController: NSArrayController!
#IBAction func sortByName(_ sender: Any) {
sortArrayController(by: "name")
}
#IBAction func sortByAge(_ sender: Any) {
sortArrayController(by: "age")
}
func sortArrayController(by key : String) {
arrayController.sortDescriptors = [NSSortDescriptor(key: key, ascending: true)]
arrayController.rearrangeObjects()
}
}

Related

How can I use a variable defined within a function in the main View Controller in another View Controller?

I'm developing a simple game (my first iOS app!) and am trying to link a variable between two View Controllers. In the first view controller, I have a textfield where the user can type in any number they choose. In the second View Controller, I would like users to be able to generate any number between 1 and the number they entered by pressing a button and be able to keep doing so. However, I am not able to use the "upperBound" variable holding the user-entered value in ViewController2.
I've tried using prepare for segue but it's not working, and I've snooped around stackoverflow and tried a couple of methods without quite knowing what I'm doing to no avail.
(UPDATED) ViewController:
class ViewController: UIViewController, UITextFieldDelegate {
//MARK: Properties
#IBOutlet weak var numberOfPages: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Handle the text field’s user input through delegate callbacks.
numberOfPages.delegate = self
}
//MARK: UITextFieldDelegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard.
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
//Save number entered and then randomly select a number within bounds
}
//MARK: Actions
var upperBound: Int?
#IBAction func setUpperBound(_ sender: UIButton) {
upperBound = Int(numberOfPages.text!)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a variable that you want to send
var newUpperBound = Int(upperBound!)
// Create a new variable to store the instance of ViewController2
let destinationVC = segue.destination as! ViewController2
destinationVC.upperBound = newUpperBound
}
}
(UPDATED) ViewController2:
class ViewController2: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
//Mark: Actions
#IBAction func roller(_ sender: UIButton) {
//Generate random number
let randomNumber = Int.random(in: 0 ..< upperBound)
}
var upperBound: Int?
}
With this code, I'm getting an error on line 34 of ViewController2 that reads "Use of unresolved identifier upperBound". Additionally, there is an issue on line 40 of ViewController that reads "immutable value upperBound was never used". I would expect to be able to generate a random value between 1 and the entered number so that I can keep working and add more features to my app (like printing these random values etc)
ViewController
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var numberOfPages: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
numberOfPages.delegate = self
}
//MARK: UITextFieldDelegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard.
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
//Save number entered and then randomly select a number within bounds
}
//MARK: Actions
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if numberOfPages.text == ""{
print("Please enter number")
return
}
let upperBound: Int? = Int(numberOfPages.text ?? "0")
if upperBound != 0{
if segue.identifier == "mySegue"{
let vc = segue.destination as! ViewController2
vc.upperBound = upperBound
}
}
}
}
ViewController2
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var lbl_UpperBound: UILabel!
#IBOutlet weak var btn_Generate: UIButton!
#IBOutlet weak var lbl_Random: UILabel!
var upperBound: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
lbl_UpperBound.text = "Upper Bound - \(upperBound ?? 0)"
btn_Generate.addTarget(self, action: #selector(roller), for: .touchUpInside)
lbl_Random.text = ""
}
#objc func roller(_ sender: UIButton) {
//Generate random number
let randomNumber = Int.random(in: 0 ..< (upperBound ?? 1))
lbl_Random.text = "\(randomNumber)"
}
}
Also Don't forget to name the Segue

Use closure to pass data between two controllers

my problem looks so simple, but since I am a beginner, I have problem to understand the concept of the closure to pass data between two controllers
for example I have a static table view controllers that has one cell and a title inside it
class FirstView: UITableViewController {
#IBOutlet weak var titleLabel: UILabel!
and I have an another view controller that contain a button inside it
class SecondViewController: UIViewController {
#IBAction func pressChangeButton(_ sender: UIButton) {
}
and there is segue1 between these two controllers, with identifier "segue1"
I want a to a simple task, I want to add a boolean closure that it will be true if the change button is pressed.
that is why I create a closure function the second view controller that has change button.
var change : ((Bool) -> Void)?
I just want, that the second view controllers tells the first one that change closure is now true (after pressing the change button) and the first view controllers simply change the title table inside it to whatever (I just want to see that how it can be done)
I don't know I have to use prepare sugue function?
Could anyone help me to understand this concept?
You can try
class FirstView: UITableViewController {
#IBOutlet weak var titleLabel: UILabel!
#IBAction func goToSecond(_ sender: UIButton) {
self.performSegue(withIdentifier: "segue1", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue1" {
let des = segue.destination as! SecondViewController
des.change = { [weak self] (value) in
print(value)
self?.titleLabel.text = "SetValue"// set a value
}
}
}
}
class SecondViewController: UIViewController {
var change : ((Bool) -> Void)?
#IBAction func pressChangeButton(_ sender: UIButton) {
change?(true)
}
}
A closure is basically a piece of code that you can run. In Swift a closure is a first class citizen as it can be passed around as parameters and return type of functions. That being said, you can pass or set a closure as you normally would for other objects.
As per Sh_Kan's answer, just set SecondViewController's closure in prepare(for segue:sender:), always paying extra attention to retain cycles. You might also want to take a look at delegate design pattern in order to exchange data and messages between your controllers.

i cannot able to pass data between viewcontrollers via protocols

View controller A
class ViewController: UIViewController {
var delegate: server?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func ok(_ sender: Any) {
delegate?.datum(data: "sd")
performSegue(withIdentifier: "goingB", sender: self)
}
}
View controller B
protocol server {
func datum(data: String)
}
class ViewControllerB: UIViewController, server {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func datum(data: String) {
self.label.text = data
print(data)
}
}
I need to pass the data via view controllers but I cannot able to pass however I know we can pass data through protocols, but anyhow I end up with error when try to run the program
If you need to pass data from one view controller to another and you're using segue for presenting new view controller, you can just override prepare(for:sender:), there is no need to using delegates. Here you can get reference for controller which will be presented and you can assign its variable.
So, first create variable in second view controller and declare that if you assign it with new value, it changes text of your label
class ViewControllerB: UIViewController {
#IBOutlet weak var label: UILabel!
var variable: String? {
didSet {
label.text = variable
}
}
}
Now in first view controller override prepare(for:sender:) and if segue is segue which you've performed, downcast destination view controller and assign its variable
class ViewController: UIViewController {
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goingB" {
let destinationVC = segue.destination as! ViewControllerB
destinationVC.variable = "sd"
}
}
}
Anyway, if you want to use your code with delegate, you have to set delegate of first view controller as second view controller which will be presented. For this purpose you can also use prepare(for:sender:) where you can get reference for destination of segue and then you can call your method on delegate
class ViewController: UIViewController {
var delegate: server?
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goingB" {
let destinationVC = segue.destination as! ViewControllerB
delegate = destinationVC
delegate?.datum(data: "sd")
}
}
}
Notes:
Name protocol with big capital letter Server and we are talking about delegates, add delegate word: ServerDelegate
Constrain protocol for just for classes
Make then your delegate variable weak
protocol ServerDelegate: class {
func datum(data: String)
}
...
weak var delegate: ServerDelegate?
The simplest here is to to set the property directly in prepare.
However, if you want to use delegate, you can. Your problem is that you have mixed between A and B.
The way you wrote it, when you call delegate?.datum, delegate is not defined and we can't access datum.
What do you want to do ? Go from A to B, and when in B, update a label in B with data received from A.
Here just to show how to use (but clearly too complex compared with direct assignment).
protocol Server {
func datum() -> String
}
class ViewControllerB: UIViewController {
#IBOutlet weak var label: UILabel!
var delegate: Server?
override func viewDidLoad() {
super.viewDidLoad()
let data = delegate?.datum()
self.label.text = data
}
}
class ViewControllerA: UIViewController, Server {
override func viewDidLoad() {
super.viewDidLoad()
}
var data = "sd"
func datum() -> String {
return data
}
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destVC = segue.destination as? ViewControllerB {
destVC.delegate = self
}
}
}

Passing data from UITableview using Protocol in Swift and Firebase

I have a tableview called PostsTableView that has a list of user posts with a UIButton called "Comment". When a user clicks Comment, I would like it to redirect to CommentViewController that has all the information around the post such as the postText and the user who wrote the post.
Snippets of the code below.
PostsTableView
extension PostsTableViewController: PostsTableViewCellDelegate {
func commentTapped(postInfo: String) {
//How do I pass postInfo along to CommentViewController
}
PostsTableViewCell
protocol TableViewCellDelegate {
func commentTapped(postInfo: String)
}
class PostsTableViewCell: UITableViewCell {
#IBOutlet weak var postTextLabel: UILabel!
#IBOutlet weak var postUserLabel: UILabel!
var postItem: Post!
var delegate: TableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func commentAction(_ sender: Any) {
delegate?.commentTapped(postInfo: postItem.postText)
}
}
Assuming you are using segues, you will call .performSegue(withIdentifier: "mySegueIdentifier", sender: self)
You could store the postInfo first by adding a variable to PostsTableViewController
var selectedPostInfo: String?
And then setting it in commentTapped(postInfo: String)
func commentTapped(postInfo: String) {
selectedPostInfo = postInfo
performSegue(withIdentifier: "mySegueIdentifier")
}
Now override controller.prepare(for: UIStoryboardSegue, sender: Any?) and set the value on CommentViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? CommentViewController {
controller.postInfo = selectedPostInfo
}
}
You can do something like this
1) Firstof all refactor the delegate method to take post object as an argument instead of just a String (This helps to provide better User Experience since no need to fetch data already available locally at that moment). Something like this
func commentTapped(postObject: Post) { // I am assuming your Post model has got all the info required by the CommentsViewController
}
2) Now create a property in CommentsViewController let's say post, which you can I initialise as below code
func commentTapped(postObject: Post) {
let commentVC = CommentsViewController.initWithNib... // Initialise the Comments ViewController
commentVC.post = postObject // Assign the post object here
show(commentVC) // Present here the commentVC as per your requirement of Modal or Push
}

Swift: Pass multiple values between views

I have a view that has two text fields and a button.
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
}
and a second view that has two variables:
var submittedURL = ""
var submittedName = ""
println("Name \(submittedName)")
println("URL \(submittedURL)")
In Swift How do I pass the values entered in the two text fields and assign them to those variables in the second view?
Thanks
EDIT FOR THETOM:
import UIKit
class ViewController: UIViewController {
#IBOutlet var inputURL: UITextField!
#IBAction func submitBtn(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as BrandsViewController
destinationVC.submittedURL.text = inputURL.text
}
}
You can use the method prepareForSegue.
In the first view (the one from which the segue is coming from) write the following code :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as CustomViewController
destinationVC.submittedURL = inputURL.text
destinationVC.submittedName = inputName.text
}
Here CustomViewController is the custom class of the UIViewController to which the segue is going to.
To perform the segue programmatically in your button #IBAction do that :
#IBAction func buttonWasClicked(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
Since your view controllers are linked with segue you can override the prepareForSegue method in first view controller and pass data by doing so
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "secondViewController") { // here secondViewController is your segue identifier
var secondViewController = segue.destinationViewController as SecondViewController // where SecondViewController is the name of your second view controller class
secondViewController.submittedURL = inputURL.text
secondViewController.submittedName = inputName.text
}
}
And to performSegue inside your button action use perfromSegueWithIdentifier method
#IBAction func submitUrlButton(sender: AnyObject) {
//replace identifier with your identifier from storyboard
self.performSegueWithIdentifier("secondViewController", sender: self)
}
The simplest way of accessing values globally not neccessary to pass with segue
First View controller
import UIKit
var submittedURL:NSString? // declare them here
var submittedName:NSString? // Now these two variables are accessible globally
class YourViewController : UIViewController
{
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
if inputURL.text == "" && inputName.text == ""
{
//Show an alert here etc
}
else {
self.submittedURL.text = inputURL.text
self.submittedName.text = inputName.text
}
}
}
SecondView Controller
import UIKit
class SecondviewController: UIViewController
{
//inside viewDidload
println(submittedURL)
println(submittedName)
}