Not all optional NSTextViewDelegate methods being fired (SWIFT) - macos

This is an OSX SWIFT project
The NSTextViewDelegate protocol defines a bunch of optional methods. I know I've correctly wired-up the delegate because I receive this notification.
func textViewDidChangeSelection(notification: NSNotification)
However, other methods I need are never being fired. Specifically, I'm trying to implement custom tooltip behaviour in a textview, but the following are never fired, even though a tooltip has been set and is being displayed.
func textView(textView: NSTextView, willDisplayToolTip tooltip: String, forCharacterAtIndex characterIndex: Int) -> String?
func textView(textView: NSTextView, doCommandBySelector commandSelector: Selector) -> Bool
There are other optional methods of interest as well, so the list above is just illustrative.
Any help would be appreciated.

Related

Dismissing keyboard in UITextField with RAC(5)?

Newbie to ReactiveCocoa and ReactiveSwfit here... Sorry if the answer is obvious.
I am trying to adapt the Start Developing iOS Apps with Swift sample to ReactiveSwift / ReactiveCocoa, and I am running into an issue with "translating" the UITextField's Delegate method -- which gets rid of the keyboard and essentially ends the editing (so I can capture the text field in the mealNameLabel) :
func textFieldShouldReturn(_ textField: UITextField) -> Bool
I am using
nameTextField.reactive.textValues.observeValues { value in
viewModel.mealName.swap(value ?? "")
}
// Setup bindings to update the view's meal label
// based on data from the View Model
mealNameLabel.reactive.text <~ viewModel.mealLabel
to get the value from the text field into the view model and percolate the view model's label back to the UILabel (convoluted...)
That works fine, as long as I maintain the viewController as the UITextField's delegate and I still implement the method depicted in the tutorial and mentioned above. Essentially :
override func viewDidLoad() {
super.viewDidLoad()
nameTextField.delegate = self
// view controller logic
...
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard.
textField.resignFirstResponder()
return true
}
I tried using
nameTextField.reactive.controlEvents
but that failed miserably due to my lack of understanding of controlEvents (docs anywhere ?).
So what do I need to do to make the keyboard disappear when the user is done editing, the "reactive way" ?
Thanks !!!
(Of course right after I post my question...)
It looks like this might actually do the trick :
nameTextField.reactive.controlEvents(UIControlEvents.primaryActionTriggered)
.observeValues { textField in
textField.resignFirstResponder()
}
After fiddling with the different event types, it looks like .primaryActionTriggered is what gets triggered when the "Done" button is pressed.
Any better way to do this ?

NSBrowserDelegate not asking for preview view controller

I'm implementing an NSBrowser in Swift and want to display a preview view controller when certain leaf items are selected in the browser.
I've implemented the func browser(browser: NSBrowser, previewViewControllerForLeafItem item: AnyObject) method, but it never gets called.
I'm also implementing the following delegate methods:
func browser(sender: NSBrowser, numberOfRowsInColumn column: Int)
func browser(sender: NSBrowser, willDisplayCell cell: AnyObject, atRow row: Int, column: Int)
previewViewControllerForLeafItem needs an item. The documentation of NSBrowser could be better but it says about previewViewControllerForLeafItem:
This method is called only if the delegate implements the item data source methods.
The item data source methods are rootItemForBrowser, numberOfChildrenOfItem, child ofItem, isLeafItem and objectValueForItem.

Global variable and optional binding in Swift

I have some quite simple doubt regarding optional binding,global variable & wrapping and unwrapping . Since I am new to SWIFT, its very important to understand the tits & bits of their concepts.
1) In Swift if I declare a global variable, I have 2 options either to make it optional or non optional, so let I am having 2-4 or more optional variables . So is it advisable to optional bind all those variables in
viewDidLoad() method// so that I could use them without any problem of unwrapping and fatal error in my program.
2) Let me make myself more clear by the following example- I have 2 VC in my project VC1 & VC2 . VC2 has a text field in which user enters some value and displays it in a tabelview in VC1.
In Vc1
var namevc1 = NSMutableArray?//holds the input of textfield to be passed from VC2.
As you can see, my VC1 is the first view controller that loads when my project runs and I am using an optional variable to populate my tabke vuew that is
'arr'
So when the app runs for the first time its empty . So it might cause a fatal error while using its value in the code. So what is its solution whether to unbind it in the
viewDidLoad()
method or in all total declare an empty NSMutable array type in place of optional type .
Thanks in advance.
I'll start by repeating the my comment from above.
Possibly you've misunderstanding the concept of global variables in Swift.
If you have a global variable, you won't have to "pass" it between any views/methods/classes etc, because the variable is defined at global scope (accessible everywhere).
Generally global variables is not a good idea, and something that you want to avoid.
Regarding the matter of global variables and swift, you really should include singletons into the discussion. See e.g. the following existing SO thread(s):
Any reason not use use a singleton "variable" in Swift?
(How to create a global variable?)
(Declaring Global Variables in Swift)
Communication between TableViewController and ViewController by means of segues (prepare for & unwind segues)
(This answer ended up being very and probably a bit too thorough, as I didn't know in detail what your current tableview/viewcontroller program state looks like. Sorry for the lengthy answer and any inconvenience it might bring to readers of it).
Now, lets leave global variables and discuss one (among other) viable options for the communication between the two controllers in your example. From your question, I'll summarize your example as follows
VC1: storyboard entry point, a UITableViewController consisting of UITableViewCells, where, in these cells, you display some text, say, via instances of UILabel.
VC2: a UIViewController, accessible from the cells of VC1, containing an UITextField instance. When user enters text into this text field, your want the text to be displayed in the associated cell in VC2 (associated in the sense that it was the cell in VC1 that was used to access VC2).
We'll associate VC1 and VC2 with (cocoa touch) classes TableViewController (TableViewController.swift) and ViewController (ViewController.swift), respectively. The cells in the table view controller will be associated with (cocoa touch) class TableViewCell (TableViewCell.swift). Details for these classes follow below.
For this simple example, note that we will not embed VC1 into a navigation controller (which is otherwise appropriate for table view -> view navigation).
We'll start in the storyboard, adding objects (drag-and-drop from object library) for a Table View Controller and a View Controller. The table view container will also, automatically, contain, in its Table View, a TableViewCell. Continuing in the storyboard:
Add a UILabel object to the TableViewCell container in the Table View Controller (align it as you wish)
In the View Controller, add a Text Field object and a Button object (align them as you wish).
Set the entry point to the Table View Controller.
Thereafter Ctrl-drag a 'Show' segue from the TableViewCell to the View Controller.
Select the Show segue and, from the Attributes inspector, enter an identifier for it, say, ShowDetail.
Finally, with the TableViewCell selected, (as above; from the attribute inspector), enter an identifier for the cell. Here, we'll use simply use identifier TableViewCell.
We now leave the storyboard for now and implement three classes, associated with the Table View Controller, the View Controller and the formers' TableViewCell.
We start with the Table View Controller, and implement our UITableViewController sub-class. Note that here, instead of using an NSMutableArray to hold the texts of the UITextLabel in each cell, we'll simply use a String array.
// TableViewController.swift
Import UIKit
class TableViewController: UITableViewController {
// Properties
var userTextLabels = [String]()
var numberOfCells: Int?
override func viewDidLoad() {
super.viewDidLoad()
numberOfCells = loadSampleTextLabels() // Load sample labels.
}
func loadSampleTextLabels() -> Int {
userTextLabels += ["Label #1", "Label #2", "Label #3"]
return userTextLabels.count
}
// func numberOfSectionsInTableView(tableView: UITableView) ...
// func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = ("TableViewCell")
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell
// Text for current cell
let cellText = userTextLabels[indexPath.row]
cell.userSuppliedTextLabel.text = cellText
return cell
}
// ... communication?
}
Where the two commented out methods are standard methods used in any UITableViewController, for number of sections (e.g. return 1) and cells (e.g. return (numberOfCells ?? 0)) in the table, respectively. I'll leave fixing these to you.
Now, we associate the TableViewCell object(s) in the table view with instances of a subclass to UITableViewCell. Here, we'll use a very simple class for our cells; each cell just containing a single UILabel instance (created via storyboard Ctrl-drag as an #IBOutlet from the UILabel in the table view cells).
// TableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
// Properties
#IBOutlet weak var userSuppliedTextLabel: UILabel!
// Ctrl-drag from UILabel (in TableViewCell) in storyboard
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Finally, for the view controller that is accessed from the table view cells: use a single #IBOutlet to the UITextField used for user text input, and handle events in this text field using the pre-existing UITextFieldDelegate. E.g.:
// ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
// Properties
#IBOutlet weak var userSuppliedText: UITextField!
// Ctrl-drag from storyboard...
var cellText: String?
override func viewDidLoad() {
super.viewDidLoad()
userSuppliedText.text = cellText ?? "..."
// Handle the user input in the text field through delegate callbacks
userSuppliedText.delegate = self
}
// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
// User finished typing (hit return): hide the keyboard.
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
cellText = textField.text
}
}
We've also declared a string property (cellText) here, that will as act as container for communication between VC1 and VC2.
We return to the storyboard and---from the Identity inspector---associate the three storyboard objects (Table View Controller, View Controller, TableViewCell) with their associated classes that we've just written above.
We're now almost at our goal; it only remains to specify how to communicate between the two controllers.
We'll begin with communication from VC1 to VC2. In your comment above, you were on the right track (for this specific solution, anyway) by looking at the prepareForSegue(...) method. In the class for the Table View Controller, we add the following method:
// ... add to TableViewController.swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "ShowDetail" {
let viewController = segue.destinationViewController as! ViewController
if let selectedCell = sender as? TableViewCell {
let indexPath = tableView.indexPathForCell(selectedCell)!
let currentTextInCell = userTextLabels[indexPath.row]
viewController.cellText = currentTextInCell // <-- note this
}
}
}
Hence, for VC1->VC2 communication, we can (in this example) bring whatever existing text that is currently occupying the UILabel in the sender cell (as is specified by the String array userTextLabels). Look at the viewDidLoad(...) method in the ViewController.swift to see how this value is passed from VC1 and set as default text in the UITextField in VC2.
Now, for communication VC2->VC1, which was the specific communication direction you were asking about, add another method (programmatically), again to TableViewController.swift:
// ... add to TableViewController.swift
#IBAction func unwindToTableView(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? ViewController,
text = sourceViewController.cellText {
// ^ note 2nd clause of if let statement above
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update cell text
userTextLabels[selectedIndexPath.row] = text
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
}
}
Here, we define an unwind action that, when triggered, retrieves the cellText property of the view controller that was the source of the segue, i.e., in our case, the instance of ViewController. But how do we trigger this action?
Return to the storyboard and the View Controller. Note the three little icons in the top of the View Controller object, more specifically, the right-most of these, named Exit. Ctrl-drag an action from your Button to the Exit icon, and select the unwindToTableView Action Segue. When you click your button the view controller, the view unwind (exit) and land at the unwindToTableView method in the TableViewController.
The resulting app should look something like this:
This was way longer than I had expected, but once you get started writing... Anyway, the method above uses, naturally, no global variables, but make use of references to future (prepareForSegue) or historic (unwindToTableView) views to get (generally from current or historic view) or set (generally in current of future view) values by using these references (to future/historic view).
Apple has their own very thorough tutorial on an example app in the tableviewcontroller/viewcontroller context that I would recommend going over. I found it very valuable myself when I started coding Swift.
Start Developing iOS Apps (Swift)

How can I disable a button until text in all UITextFields has been entered in swift?

I need a way to disable the save button until text has been entered in all of the required text boxes? I am developing the application in Swift and I have found lots of answers in Objective-c. As I have absolutely now Objective-c knowledge, I am unable to work out what it means.
Does anybody have a solution for this which can be done in Swift?
I know how to enable/disable a button. I also know how to check if a text field is empty. I'm just not sure how to make it so that my code is always checking to see if it is empty or not. I have tried a while loop but as I expected, this froze everything.
Listing one of the ways to achieve this:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var button: UIButton!
//Need to have the ViewController extend UITextFieldDelegate for using this feature
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Find out what the text field will be after adding the current edit
let text = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
if !text.isEmpty{//Checking if the input field is not empty
button.userInteractionEnabled = true //Enabling the button
} else {
button.userInteractionEnabled = false //Disabling the button
}
// Return true so the text field will be changed
return true
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Setting the Delegate for the TextField
textField.delegate = self
//Default checking and disabling of the Button
if textField.text.isEmpty{
button.userInteractionEnabled = false // Disabling the button
}
}
}
Reference Link for the above solution
Despite of all the comments given so far and to not blow up the comment area further, I try to give some hints on how to solve your problem:
Put all the textfields in an outlet collection
Set the delegate of all textfields to your viewController
implement the delegate's didEndEditing method and within that method iterate over the outlet collection to check each textfield for its input
Note that this is only ONE way to implement that but you might get the idea.
Use the textfield's delegate methods (or the target-action pattern) to check the conditions required for the user to proceed. If they're met, enable the button.

How do I subscribe to TextField's TextChanged event in Xcode

I've recently started working on some test projects to get the feel for OS X development with Xcode. I come from Windows, so I might not be making much sense here.
How would I subscribe to certain "events" in Swift? I have just learned how to connect actions to UI objects. For example, I can now click a button, and change the text of a label programatically. However, and this may just be a case of lack of knowledge on my part - I am not able to find a way to subscribe to a TextField's "Text Changed" event.
Let's say that I have a TextField, and when I change the text at runtime (i.e. type something), I want to do something in the textChanged event for that particular TextField.
Is there even such a thing as a TextChanged event in OS X development?
Update
I am now using the following code:
import Cocoa
class ViewController: NSViewController {
class textField:NSTextField, NSTextFieldDelegate
{
override func awakeFromNib() {
delegate = self;
}
override func controlTextDidChange(obj: NSNotification)
{
println("Text changed.")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
And I have added a ClassName to the TextField control in the Identity Inspector, but it isn't responding to the text changing. The message given is:
Failed to connect (textField) outlet from
(Xcode_Action_Basics.ViewController) to (NSTextField): missing setter
or instance variable
I just googled that error and came across this page: Failed to connect (storyboard) outlet from (NSApplication) to (NSNibExternalObjectPlaceholder) error in Cocoa and storyboard which states that this is a known issue in Xcode and that it does not mean there is a problem with your code - but I'm not so sure about that, because the code isn't working. Not sure if I've missed out on something.
Create a class that implements the protocol NSTextFieldDelegate like
class MyTextField:NSTextField, NSTextFieldDelegate {
override func awakeFromNib() {
delegate = self // tell that we care for ourselfs
}
override func controlTextDidChange(obj: NSNotification) {
// .... handle change, there are a lot of other similar methods. See help
}
}
In IB assign this class here:

Resources