OK, so I'm working on this OS X application in Swift with Xcode 7 and I can't figure out how to save a string. I want to save a string, so if I write I something in a text field in my application, it will save that string and if I close my application and reopen it that string will still exist and will contain everything that it had before I closed my application.
I hope I make sense.
You can use NSUseDefaults to make your field persist through launches. Just ctrl-click your text field and add a IBAction to it that will be called when the user ends editing the text field. Just save the text field (sender) stringValue there, connect one IBOutlet also to your text field and use NSUserDefaults stringForKey method to load your string back inside viewDidLoad method:
import Cocoa
class ViewController: NSViewController {
#IBOutlet var textField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.stringValue = NSUserDefaults().stringForKey("textFieldKey") ?? "default value"
}
override var representedObject: AnyObject? {
didSet {
}
}
#IBAction func textFieldAction(sender: NSTextField) {
NSUserDefaults().setObject(sender.stringValue, forKey: "textFieldKey")
}
}
If you just want to save simple data, such as a few strings, you can use NSUserDefaults. Check out a tutorial here.
If you want to save complicated data, you should use Core Data. It seems harder to learn at the beginning but you will enjoy its convenience and performance later. I learned Core Data here.
Related
I just spent couple of days for trying to figure this out for mac app, but not doing it successfully.
I have first ViewController and second ViewController. Let's say, ViewControllerA(VC-A) and ViewControllerB(VC-B). In Main.storyboard, they are connected by segue with identifier "PromptToUser".
VC-B gets triggered not by button, but if condition during the process.
It is triggered by:
performSegue(withIdentifier: "PromptToUser", sender: self)
in VC-A.
In VC-A, I put the followings so far:
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
}
In VC-B, so far, I have:
#IBOutlet weak var Login: NSTextField!
#IBOutlet weak var Pass: NSSecureTextField!
VC-B also has a button which I want to make as a trigger to collect information entered by user for above variables and send it back to first view.
I found many similar topics on this in stackoverflow. However, it seems most of them are for iOS, but not for mac app as I could not make UI... and other classes (delegates) and protocols they mentioned to be recognized for Swift 3 environment. I have NSViewController, and NS... not UI...
What would be the best way to approach this for mac app with Swift 3?
You Can make a static variable in VC-A and just update the value of static variable from VC-B. Example
In VC-A
static var login = ""
static var password = ""
In VC-B
VC-A.login = nameTextField.text
VC-A.password = passTextField.text
Write the code in SaveButton Action of VC-B, and you will get the update value in VC-A.
If You are not using any Save Button in VC-B, then You can write code in viewWillDisappear Method of VC-B
override func viewDidDisappear(_ animated: Bool) {
firstVC_A.login = nameTextField.text
firstVC_A.password = passTextField.text
}
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 ?
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.
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:
I'm trying to add a new sub view form a nib using swift for OS X.
So far i've:
created a new "Cocoa Application"
added a new "Cocoa Class" called "TestSubView" as a subclass of NSViewController with a XIB file
I want to add this subview to my main view when the application loads.
in my ViewController ( the ViewController for the main window ) i have.
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let newSubView = TestSubView();
self.view.addSubview(newSubView.view);
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
But i'm getting the following error
Failed to set (contentViewController) user defined inspected property on (NSWindow):
-[NSNib initWithNibNamed:bundle:] could not load the nibName: temp.TestSubView in bundle (null).
I realise i will need to size and position this subview but I can't seem to get to that point.
I've spent the better part of a day trying to figure this one out so any help would be greatly appreciated.
I finally got this thing to work. My new code looks like
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let subview = TestSubView(nibName: "TestSubView", bundle: nil)!
self.view.addSubview(subview.view)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Found with the help of the docs & this answer
It was suggested that if the nib name and the class name are the same you shouldn't need to declare nibname: (as i'd tried to do originally) but the docs didn't mention this - explains why it didn't work!
For prosperity, this worked for me with Xcode 6.1.1 on OS X Yosemite (10.10.1)
A nib is really nothing but an XML file with view information in it. You have to get it from the application bundle and get one of the views contained in it explicitly. You are perhaps confounding views and view controllers (your attempt to extract view from newSubView suggests that).
Try this:
let subview = NSBundle.mainBundle().loadNibNamed("TestSubView",
owner:self, options:nil)![0]! // maybe no final unwrapping "!" in Swift 3
self.view.addSubview(subview)
Make sure the xib is really called the name you are using and contains at a least one view (otherwise the two unwrapping ! above will crash your app).