I have a simple Document based Core Data app (built around the standard Apple tutorials). At the moment I have a button connected to the add: method of a controller (Sheet Controller) that drops down a modal sheet from the main document window. The connection is made in IB (see below from MyDocument.xib). There is also another controller (Another Controller)
My question is how do I programatically change the buttons action method to point to a method in 'Another Controller' instead of the 'Sheet Controller it is wired up to in IB
Many Thanks
Just use the NSControl setTarget and setAction methods.
There is some special magic associated with making the connections hang together in the first place, but once it's all wired up in the running application a connection is a connection regardless of how it was originally made.
Although -- it's worth considering whether you could allow the responder chain to take care of things rather than explicitly retargeting the action yourself. That, at least in part, is what it's there for...
Related
Why is it that when a WKInterfaceButton is connected to both a push segue and an action, the action isn't called?
When you perform a segue, the old InterfaceController goes off screen, of course. WatchOS seems to destroy the bridge between your extension and the App (the storyboard/interface). The same thing happens in the other direction. The extension can't modify UIs that aren't on screen. An example is UI properties. Try setting the color of a label after the interface controller goes off screen. It won't work.
From the docs:
Important
An interface controller can make changes to its interface only during initialization and when the interface is active. Once the didDeactivate() method is called, any attempts to change the value of related interface objects are ignored until the interface controller’s willActivate() method is called again.
Presumably you're an iOS dev. It might be good to read through https://developer.apple.com/reference/watchkit/wkinterfacecontroller
Since I started learning watchOS, a lot of my assumptions/experience did not translate well over to the watch.
I am relatively new to Cocoa/Xcode and am not sure if I am structuring my application in the most "correct" way possible. I have a number of objects which are active the while time that the application is running. Should I be creating these objects manually or with Interface Builder?
The type of objects I am referring to:
"downloader" which is responsible for downloading files to disk
user interface updater which is responsible for updating the user interface to show the results of the downloaded file
Should I create these objects in IB and set up the connections between them with code?
It's really a matter of personal preference.
In my opinion IB only really good at laying out views, so I tend to only use IB for my view and my view controller, and I create everything else in code in the view controller's viewDidLoad or init method.
In your example, connecting the "downloader" object directly to the interface seems like an MVC violation, so I would keep the downloader out of my xib.
The "interface updater" would be connected tightly to the interface, so it could be in the nib, although unless I had a good reason not to I would probably just put that code into my view controller class.
If you are creating things in code, note that viewDidLoad/viewDidUnload can potentially be called a number of times, as the os loads and un-loads your views when they are not visible to save memory. So only put transient objects there... things that must exist for the life of the view controller should be created in the init/dealloc methods. Part of why I like to do most of my object creation in code, is the finer level of control you have over memory.
I typically build as much as I can in IB, and then switch to code when I run into the limitations of IB. It sounds like you should be able to create the UI you described in interface builder.
I am quite new to Cocoa and to iPhone programming.
I am using the Xcode Utility Application template to implement a simple app that has:
a view with a text field to collect a username
a view with a connect button to start a connection to a remote site using the
username to get some data via HTTP. The data will be presented as a text string on the screen.
I think this represents my VIEW in the MVC pattern.
I created a simple class to store the username and to do all connection work that represents my MODEL and instantiated it inside the AppDelegate.
Here a really simplified sketch:
It is not really clear to me how can I get data nested deep into subviews (username) or how can I trigger actions in nested parent views (connect button).
My question is:
What is the best/cleanest way to implement this architecture?
How do I implement the CONTROLLER?
Thanks in advance for any help,
Paull
Updating my answer based on comment:
It's in most cases ok to have state in your controller. Like an array or an instance of whatever modelobject you are writing an application for.
I would keep the model object clean of any networking code and put that in the Controller instead though. In this case the ViewController where that connection action is triggered.
Original answer:
It is not really clear to me how can I
get data nested deep into
subviews(username) or how can I
trigger actions in nested parent
views(connect button).
With the utility application template you already have a couple of ViewControllers.
To get references to your UI inside your controllers you need to declare IBOutlets and connect them inside Interface Builder. To respond to actions you need to declare and implement IBActions in your ViewControllers and hook them up in Interface Builder as well. Which you do in Connections pane (2nd from left) in the inspector.
I have a simple document-based Cocoa app that acts as a viewer for .wav files, to do a bit of frequency analysis. I'd like to be able to export the data gleaned from opened files to CSVs for further analysis in other programs.
The document-based application framework in Cocoa lets you override
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
to implement the default Save/Save As... workflow, but I don't want to write the files that I open.
The obvious thing to do is implement an export workflow in my document, to present a file save sheet, build some NSData, and write it to a file path, but there is not an obvious way to connect an outlet in the MainMenu nib to an action on a document controller.
So, then, what is the accepted way to implement such functionality in a document-based Cocoa app?
You can create a new action in MainMenu.nib's "First Responder" object, called "export:", and connect to it. Then, implement an export: method in your document subclass. This will call your method.
The reason this works is that messages sent to the magic first responder object go through the entire responder chain, looking for some object that handles them. One of the items in that responder chain is the document, and so when the currently selected control, view, superview, window, etc, all do not handle the message, the document gets a chance. (The document controller is also on that chain, so you could use it, too.)
Take a look in Apple's responder chain docs - figure 1.10 covers this particular path.
I want to load some data from mysql into my cocoa application view before the application starts.
I am sure that this should happen in the controller so that it can send the required data to the view.
I am looking for a method or common technique that is used for this sort of thing.
Many Thanks
Sounds like you're looking for the awakeFromNib function.
http://www.cocoadev.com/index.pl?AwakeFromNib
Cocoa gives you many places to perform tasks before and after objects are loaded from a nib, but it's important to read the documentation carefully to make sure things are happening in the order you expect. Usually I use the following strategy when I'm working on a Cocoa application:
Where appropriate I implement the
+(void)initialize method, which is called before any instances of a class are created. I'll probably set the app's default preferences here, for example.
In my application controller (app delegate), I implement the applicationDidFinishLaunching: delegate method to load my data file. If this works okay, I then create the window controller(s) and display any windows I want to show at launch.
In the window/view controllers, I override windowDidLoad: or loadView to perform tasks involving objects loaded from a nib. If I need to create any instance variables that don't involve the nib, I also override the init method and do that there.
If I need to do anything in my view objects after they're loaded from a nib, I'll override awakeFromNib.
You can use the - applicationDidFinishLaunching: or - applicationWillFinishLaunching: delegate messages, by implementing one of them in your application delegate/controller, and do whatever initialization you want there.