Forgive the super noob question, but I've been googling this for the past hour and am getting frustrated that I can't seem to find an answer to such a very basic question:
How do I handle control changes in Cocoa?
I'm coming from iOS, and it's clear that Cocoa does not use outlets or delegates to handle events in the same way that UIKit does and I feel like I'm just missing some very important piece of information here. I've figured out that it uses the first responder chain, but beyond that I can't figure out how to actually do it, or how to even find where these events are defined or documented.
So I have a NSSegmentedControl inside a NSToolBar and I just want to know when a user changes the selected segment. I've pored through the class documentation but cannot see a single mention of any kind of events or actions to handle. I did notice if I drag the control's action outlet onto my first responder proxy, I get a quadrillion different actions listed, none of which seem relevant in any way.
How on earth do I do this in Cocoa?
This is a code from one of my projects
var currntSeg : Int = 1
#IBOutlet weak var acSwitch: NSSegmentedControl!
#IBAction func SwitchButton(_ sender: AnyObject) {
switch acSwitch.selectedSegment {
case 0:
currntSeg == 0 ?
self.navVC?.pushViewController(Sleeper!, animated: true) :
self.navVC?.popViewController(Sleeper!, animated: true)
case 1:
currntSeg < 1 ?
self.navVC?.pushViewController(Work!, animated: true) :
self.navVC?.popViewController(Work!, animated: true)
case 2:
currntSeg < 2 ?
self.navVC?.pushViewController(Student!, animated: true) :
self.navVC?.popViewController(Student!, animated: true)
default:
self.navVC?.pushViewController(Rose!, animated: true)
}
currntSeg = acSwitch.selectedSegment
print("Selected Seg: \(acSwitch.selectedSegment)")
}
Related
I tried to create a simple Complex Number Calculator using classes. My application has compiled successfully, but when I ran it, a blank window appeared instead of a window with all my buttons, labels etc. and I got this message in the output window:
2016-03-08 22:20:42.499 Complex Numbers[30404:2328250] Failed to set
(contentViewController) user defined inspected property on (NSWindow):
Cannot create BOOL from object <_NSControllerObjectProxy:
0x6000000022c0> of class _NSControllerObjectProxy
This is my ViewController class code. It involves a complexNumber class, which I didn't submit here:
class ViewController: NSViewController {
#IBOutlet var Screen: NSView!
var a = complexNumber();
#IBOutlet var realValue: NSTextField!
#IBOutlet var imaginaryValue: NSTextField!
#IBOutlet var resultLabel: NSTextField!
#IBAction func lengthResult(sender: AnyObject) {
let r = NSString(string: realValue.stringValue).doubleValue;
let i = NSString(string: imaginaryValue.stringValue).doubleValue;
a = complexNumber(real: r, imaginary: i);
resultLabel.stringValue = String(a.trigonometric());
}
#IBAction func trigonometryResult(sender: AnyObject) {
let r = NSString(string: realValue.stringValue).doubleValue;
let i = NSString(string: imaginaryValue.stringValue).doubleValue;
a = complexNumber(real: r, imaginary: i);
resultLabel.stringValue = String(a.length());
}
#IBAction func operation(sender: AnyObject) {
a = complexNumber(real: NSString(string: realValue.stringValue).doubleValue, imaginary: NSString(string: imaginaryValue.stringValue).doubleValue);
realValue.stringValue = ""
imaginaryValue.stringValue = "";
let b = complexNumber(real: NSString(string: realValue.stringValue).doubleValue, imaginary: NSString(string: imaginaryValue.stringValue).doubleValue)
switch sender.stringValue {
case "+": a = a.sum(b)
case "-": a = a.dif(b)
case "x": a = a.mul(b)
case ":": a = a.div(b)
default: a = a.sum(complexNumber())
}
}
#IBAction func displayResult(sender: AnyObject) {
resultLabel.stringValue = String("\(a.real) + i*\(a.imaginary)");
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
I found a similar thread here, but I don't think it's what I was looking for.
Can you help me, please?
Another reason - when you setup wrong binding.
Example of my error:
Failed to set (contentViewController) user defined inspected property on (NSWindow): [<NSProgressIndicator 0x10111b890> valueForUndefinedKey:]: this class is not key value coding-compliant for the key Enabled.
To solve this you need to delete the binding here:
For me was a timing issue I guess. All begins after I added an SFAuthorizationView some days ago, and I discovered that thanks to a bug report, where also was clear that this is happening on older OSes like Sierra, but it is fine, instead, in 10.13+.
Moving some code from viewDidLoad() to viewDidAppear() the problem gone.
Basically I'm just waiting to call any related method of the problematic view later the viewcontroller content view is declared as loaded. Clearly a an Apple problem fixed in new OSes. So after instantiate the nib/xib/storyboard involved I think anyone encounter problems like that should firstly show the view and then customise it. Just my testimony.
I had this problem and figured it out.
In my view I had a user defined property (if you look at the top of your view where you have view controller and first responder you should see it's icon next to it).
Simply delete it and run your application.
Hope this helps!
I you've created an app with a"storyboard" i.e. if there's a storyboard file with your views and windows in it, then there's one of two things missing:
1) If theres a main window and a view that should be it's main view, then right click - drag from the window controller to the view that should be the main view.. When the popup happens, click on "content view" i.e. like follows:
Check out my video example
If this fails, then I'll dig out plan two :)
How this helps!!
Ade.
One thing that may help others, I saw the same message, it looked like the contentViewController was the problem, but it turned out it was another component in something I was writing was failing. It seems the window manager catches all exceptions, not just window exceptions, and prints this deceptive message. What worked for me is stepping through to find component is not loading.
This happens when there is an error or exception in ViewDidLoad.
Ensure that error is cleared and your UI will load fine and u wont get this message.
Ok so here is my question.
I want to have a screen pop up right after my LaunchScreen exits and i want to be able to add information into it, however once I add that information into it, I do not want to see that screen again.
I will be using storyboard, swift and CoreData and Xcode 6.3 if that helps.
Thanks in advanece
NSUserDefaults is a really quick way of storing (small amounts of) data.
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(true, forKey: "hasSeenStartup")
if(defaults.boolForKey("hasSeenStartup")){
println("Don't show")
}
First you will need to define a set of userDefaults, easiest is standardUserDefaults()
We can set a boolean value to a key (after we showed the popup screen) using setBool() this takes 2 parameters, the first is the boolean value, the second is a key for the value.
When the users launches the app, you will need to get the boolean value for the key. If the user already has seen the popup (it’s boolean value for hasSeenStartup will be set to true) you can skip the popup and show them the other viewController
EDIT:
Since your question seems to focus on showing the new view controller rather than saving the data. Here is some code on how to show a new view controller.
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: NewViewControllerClass = storyboard.instantiateViewControllerWithIdentifier(“newViewController") as! NewViewControllerClass
self.presentViewController(vc, animated: true, completion: nil)
To use this code you will need to change the:
Storyboard name to the name of your storyboard (defaults to “Main”)
Set the class of the viewController by replacing NewViewControllerClass with the class of (obviously) your new viewController.
Change the newViewController with the Identifier
You can than use this code to present the new ViewController. Present this viewController if your Core Data has info saved about the user and thus if the user has seen the initial screen.
I would advice to retrieve the Core Data inside the ViewDidAppear method inside your FirstViewController.
override func viewDidAppear(animated: Bool) {
if (check to see if core data is not empty){
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: NewViewControllerClass = storyboard.instantiateViewControllerWithIdentifier(“newViewController") as! NewViewControllerClass
self.presentViewController(vc, animated: true, completion: nil)
}
}
I'm building a typical Xcode 6 iOS app.
My goal is:
A screen that has an sub-area that can be swiped to change the content.
For example, the home screen has a logo image, a middle area that I want to be swipeable, and a bottom button.
When the user swipes (or taps) the middle area, the area shows the next (or previous) information, which is a typical UIImage and UILabel caption.
The rest of the screen stays the same, i.e. there is no navigation change.
The code is here. It use the recommendations from the StackOverflow post here.
My question: how can I implement the code below better, while still using an XIB?
My current implementation does work, and uses this approach...
A typical Swift Demo.swift file that is a UIViewController that has:
the page index, min, and max
outlets for the PageControl, UIImageView, and UILabel
actions for the page control change, and the image swipe or tap
A typical Demo.xib file that has:
a typical UIViewController for the entire screen
a UIImageView and UILabel for the changeable image and caption text
a PageControl to indicate what tutorial page the user is viewing
I am seeking better ways to accomplish this; I've read many of Xcode tutorials and so far none seem definitive for Xcode 6, XIBs, and Swift.
Here are some implementations that I've researched that seem promising...
Is there a way to implement a subview area in the XIB?
For example, can Xocde show the XIB with a rectangular area that is intended for the changeable content?
Is there an idiomatic way to write the code for changeable content?
For example, by using a ScrollView, perhaps that contains a UIPageViewController?
Is there a way to make a PageControl XIB object large enough to cover the entire UIImageView and UILabel, so I can skip making the UIImageView respond to gestures.
In my Xcode, the PageControl seems to have an uneditable height that is always 37.
The bounty will be for expert advice.
To make a UIPageViewController swipe-able you should implement the UIPageViewControllerDataSource protocol and provide a view controller for the pageViewController(pageViewController:viewControllerBeforeViewController) -> UIViewController? and the ...viewControllerAfterViewController) methods.
Provide a custom view controller for each page that presents an image and label and takes them as properties so you can provide them from the PageViewController.
My trick it to create a method that instantiates a new view controller in these methods:
// MARK:- UIPageViewControllerDataSource
extension MyPageViewController: UIPageViewControllerDataSource {
func viewControllerWithIndex(var index: Int) -> UIViewController! {
let viewController = storyboard?.instantiateViewControllerWithIdentifier("MyViewController") as! MyViewController // This VC has to be in the storyboard, otherwise just use MyVC()
// Adjust the index to be cyclical, not required
if let count = data?.endIndex {
if count == 1 && index != 0 { return nil }
if index < 0 { index += count }
index %= count
}
viewController.view.tag = index
viewController.record = data?[index]
return viewController
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let index = viewController.view?.tag ?? 0
return viewControllerWithIndex(index + 1)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let index = viewController.view?.tag ?? 0
return viewControllerWithIndex(index - 1)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return countAndSetupPageControl()
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return viewController?.view.tag ?? 0
}
}
Now for the "sub-area" you will need to implement a ChildViewController. If you're using storyboards you can just drag a Container View and put PageViewController in the embedded view controller, otherwise you need to add the PageViewController.view as a subview and set the frame to the middle.
You can find more info in the apple documentation but basically you MUST call these methods:
addChildViewController(pageViewController)
view.addSubView(pageViewController.view)
pageViewController.view.frame = ... // This is your "sub-area"
pageViewController.didMoveToParentViewController(self)
If you add a height constraint to PageControl you can set it's height to whatever you want.
I don't see a problem with your current implementation. Changing it to use a PageViewController would be quite more work.
If I were you I would add an animation in pageUpdate function so the image would fade in or slide in...
It would only make sense to use a PageViewController if you want to be able to scroll to the next page (as in content moving in the same time your finger is moving onscreen). And you can use a PageViewController or a CollectionView with paging enabled.
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'm a complete beginner to Swift, so this may be a silly question, but I can't figure out how this works...
I have a view with a button inside which calls the following code:
let window = NSWindow()
window.center()
window.title = "test"
window.makeKeyAndOrderFront(self)
When I click the button the window opens just for a moment and disappears a few milliseconds later.
Can anyone help me with that? It seems I have a quite serious misunderstanding about views in Cocoa ;-)
Thanks
Tom
The problem is that you are creating and 'storing' the NSWindow in your button action function. That means that as soon as that button action is done, the NSWindow will go out of context, and be released and thus disappear.
This is how the memory management in Swift works: as soon as nobody owns an object anymore, it will be released.
What you should do is put your window in an instance variable. Like for example:
class YourViewController: NSViewController {
private var window: NSWindow!
#IBAction func buttonAction(sender: UIButton) {
window = NSWindow()
window.center()
window.title = "test"
window.makeKeyAndOrderFront(self)
}
}
The hint about makeKeyAndOrderFront(nil) makes no difference. Passing either nil or self is fine. But latter, how you did it originaly, makes more sense.