I learn Cocoa Touch several days, and today have stuck while looking for way to implement a custom event. Event that I can see in Connection Inspector for my UIView subclass.
What I have:
There are a UILabel and MyView:UIView on MainVindow. MyView contains a UISlider. Interfaces for Controller and MyView
// Controller.h
#interface Controller : NSObject {
IBOutlet UILabel *label;
IBOutlet MyView *myView;
}
// I suppose that there should be something like -(IBAction) changeLabelValue for myView event
#end
// MyView.h
#interface MyView : UIView {
IBOutlet UISlider *slider;
float value;
}
- (IBAction) changeValue; //for slider "Changed Value" event
What I want:
Add something in MyView that allows it to rise a event after change value.
Can anybody help me? My main area in programming is .NET and I begin think that its terminology is not appropriate for this case.
Thanks.
I don’t know if I'm understanding you correctly but I think what you want is responding to user events from interface components. In Cocoa the term "event" is only used for objects that describe the actual event, like a touch down or key up.
To respond to higher level events, like dragging a slider or pushing a button, Cocoa uses the target action paradigm. You set up a UI component (a UIControl derived view class) to send a given message to a given target whenever the component detects a change of its state.
To set the target and the action method you can use Interface Builder or the UIControl method addTarget:action:forControlEvents:.
Related
I'm going through this book called "cocoa programming for mac os x" and I just started with delegates. This whole thing with delegates is still a little bit wacky to me but I think I just need to let it settle.
However there was this one exercise where I should implement a delegate of the main window so that if resized height is always 2xwidth.
So I got 4 files:
AppDelegate.h
AppDelegate.m
WindowDelegate.h
WindowDelegate.m
AppDelegate are just the two standard files that get created when you open a new Cocoa project. I had to look up the solution because I didn't quite know how to accomplish this task.
The solution was just to create a new cocoa class, "WindowDelegat.h/.m" and add this to it's implementation file:
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
newSize.height = newSize.width * 2;
return newSize;
}
Then I opened the interface builder, added a new object and made it my WindowDelegate. I then had to ctrl drag from the WindowDelegate to the actual window and made it the window's delegate.
Clicked run and it worked. Yay! But why?
First I thought that "windowWillResize" is just one of these callback functions that get's called as soon as the window is resized but it isn't. Normally methods get invoked because the general lifecycle of an program invokes them or because they are an #IBAction, a button or different control elements.
But "windowWillResize" is non of them. So why is it called?
EDIT: Problem solved! Thanks a lot!
Now I'm trying to connect the delegate to the window programmatically. Therefore I deleted the referencing outlet from WindowDelegate to the actual window in interface builder. It works but I just want to verify that this it the correct way how it's done:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "WindowDelegate.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#end
AppDelegate.m
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) WindowDelegate *winDeleg;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (void)awakeFromNib {
[_window setOpaque:NO];
NSColor *transparentColor = [NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.5];
[_window setBackgroundColor:transparentColor];
NSSize initialSize = NSMakeSize(100, 200);
[_window setContentSize:initialSize];
_winDeleg = [[WindowDelegate alloc] init];
[_window setDelegate: _winDeleg];
}
#end
WindowDelegate.h
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#interface WindowDelegate : NSObject <NSWindowDelegate>
#end
WindowDelegate.m
#import "WindowDelegate.h"
#implementation WindowDelegate
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
newSize.height = newSize.width * 2;
return newSize;
}
- (id)init {
self = [super init];
return self;
}
#end
Why does the #property of WindowDelegate need to be strong?
And isn't my winDeleg an object? Why do I have to access it through _winDeleg when it's an object. I though the underscore is used to access variables?
Thank you for your help!
Clicked run and it worked. Yay! But why?
Because instances of NSWindow have a delegate property that can point to any object that implements the NSWindowDelegate protocol, and that protocol includes the -windowWillResize:toSize: method.
Read that a few times. The reason it's important is that you can create your own object, say that it implements NSWindowDelegate, implement -windowWillResize:toSize:, and set that object as your window's delegate. Then, whenever the user resizes the window, your method will be called and can modify the proposed new size.
Normally methods get invoked because the general lifecycle of an program invokes them or because they are an #IBAction, a button or different control elements. But "windowWillResize" is non of them. So why is it called?
This really isn't so different. Think of delegates as "helper objects." They let you customize the behavior of an object without having to create a whole new subclass. The NSWindowDelegate object is essentially a contract that the NSWindow promises to follow: whenever certain things happen, such as the user resizing the window, the window will call certain methods in its delegate object, if the delegate exists and implements those methods. In the case of NSApplication, a lot of those delegate methods are application lifecycle events, like the app starting up or quitting or getting a message from the operating system. In the case of NSWindow, delegate methods correspond to interesting events that can happen to a window, like the user moving it, hiding it, showing it, maximizing it, moving it to a different screen, etc. Other classes, like text views or network connections or movie players, have their own sets of interesting events and their own delegate protocols to match.
Note that methods marked IBAction really aren't delegate methods, they're just methods that get called by objects like controls that use a target/action paradigm. The IBAction keyword lets the IDE know which methods it should present as possible actions for things like buttons. You often find actions in window controllers and view controllers, and those objects frequently act as a delegate for some other object, but the actions themselves aren't part of the delegate protocol. For example, NSTableView takes a delegate object that determines how the table will act and what's displayed in it. It often makes sense for the view controller that manages the table to be the table's delegate, and that same view controller might also manage some buttons and contain the action methods that said buttons trigger, but the actions aren't part of the NSTableViewDelegate protocol and you therefore wouldn't call them delegate methods.
I have an NSTextField subclass (called "txtField1" and used as Custom Class for a Text Field in my interface builder) and I would like to be able to access an NSComboBox object which present in my interface builder from this class.
This is my code:
txtField1.h:
#import <Cocoa/Cocoa.h>
#interface txtField1 : NSTextField
#end
txtField.m:
#import "txtField1.h"
#implementation txtField1
-(void)mouseDown:(NSEvent *)theEvent
{
HERE I would like to be able to write something like:
[combobox SetHidden:YES];
}
#end
I would like to be able to set access the combobox SetHidden property, in the mouseDown event.
Can you please tell me how to do that? I have tried different solutions found on internet but didn't obtain anything at all!
Any help would be appreciated.
Here are a lot of ways, and answers here, to do :
Update a label through button from different view
Xcode - update ViewController label text from different view
Setting label text in another class
Set label on another view to stored NSDate
EDIT:
-(void)mouseDown:(NSEvent *)theEvent
{
HERE I would like to be able to write something like:
[combobox SetHidden:YES];
/*
use the shared instance of comboBox here and make it hidden.
Also, you can use binding to make it hidden
*/
}
From my point of view txtField1 class is not better place to this code.
You can add NSControlTextEditingDelegate protocol to your NSViewController implementation (that already contains IBOutlets for txtField1 and combobox) and in method – control:textView:doCommandBySelector: implement hiding of your NSComboBox
I have a UIViewController containing a UIImageView that has been correctly wired up (I can set image inside controller).
I have also set an iVar using
UIImageView *_imgQRCode;
and a property using
#property (strong, nonatomic) IBOutlet UIImageView *imgQRCode;
Synthesized using:
#synthesize imgQRCode = _imgQRCode;
But when I call something like below I nothing happens the image and when I inspect in Xcode the reference is memory reference is 0x00000000. note the code works when called inside the ViewController. The following is called from any other view controller with controller instantiated as follow:
QRPopupViewController *qrPop = [[QRPopupViewController alloc] initWithNibName:#"QRPopupViewController" bundle:nil];
[controller.imgView setImage:[UIImage imageNamed:#"sample.png"]];
Any ideas how to fix this and why i'm getting null?
Firsty: outlets should be weak
#property (weak, nonatomic) IBOutlet UIImageView *imgQRCode;
You have to hook imgQRCode outlet to your viewController, and you can use it inside viewDidLoad.
You will get null for all UI Control's instances which are not part of the currently visible view controller. So due to this all your calls to that UI control will not perform the UI related operation.
If you want to update any UI control which is not part of currently visible view controller, you can use app delegate. It is the only place which is centralized in between all view controllers.
There is a mechnism, called "protocol delegate" to call some method to update UI part from the currently visible view controller. You can go through this URL for detail about this.
If you are selecting the approach of "protocol delegate", then explore the method execution flow in detail. When you call a protocol from a method of currently visible view controller, the corresponding protocol implementation method will be called after the completion of the caller method. So you need to take care of synchronization.
What I've done so far is working but I would like to know whether this is the proper way or not.
I have a map that shows an annotation when this is pressed shows a callout.
The next view shown is a table view. This table has a button to remove that annotation.
I created one property in the table View of type MKMapView. After this view is initialized when the callOut accessory is tapped, I set the MKMapView property.
When the button is pressed in the table view, I delete the annotation through the map property.
Is this the right way?
Rather than the detail view directly manipulating the parent (map) controller view's controls, a more "right" approach might be to use delegate+protocol.
Define a protocol with the methods that the map controller needs to implement (eg. deleteAnnotation, detailViewDone, etc).
The detail view will have a delegate property for that protocol and call the protocol methods via the delegate property instead of directly accessing and modifying another view's controls.
The map controller would set itself as the delegate of the detail view and actually implement the protocol methods.
This way, each controller/class doesn't have to know the internal details of how the others work and let's you more easily change how each one works internally without affecting code in the others (as long as the protocol doesn't change). It improves encapsulation and reusability.
For example, in the detail view .h, define the protocol and declare the delegate property:
#protocol DetailViewControllerDelegate <NSObject>
-(void)deleteAnnotation:(id<MKAnnotation>)annotation;
-(void)detailViewDone;
//could have more methods or change/add parameters as needed
#end
#interface DetailViewController : UIViewController
#property (nonatomic, assign) id<DetailViewControllerDelegate> delegate;
#end
In the detail view .m, wherever you handle the delete button, call the delegate method instead:
if ([delegate respondsToSelector:#selector(deleteAnnotation:)])
{
[delegate deleteAnnotation:annotation];
}
In the map controller .h, declare that it implements the protocol and declare the methods:
#interface MapViewController : UIViewController<DetailViewControllerDelegate>
-(void)deleteAnnotation:(id<MKAnnotation>)annotation;
-(void)detailViewDone;
#end
In the map controller .m, in calloutAccessoryControlTapped where you create the detail view, set the delegate property instead of the map view property:
DetailViewController *dvc = [[DetailViewController alloc] init...
dvc.annotation = view.annotation;
dvc.delegate = self;
[self presentModalViewController:dvc animated:YES];
Finally, also in the map controller .m, implement the delegate method:
-(void)deleteAnnotation:(id<MKAnnotation>)annotation
{
[mapView removeAnnotation:annotation];
//dismiss the detail view (if that's what you want)...
[self dismissModalViewControllerAnimated:YES];
}
From the documentation, the articles Delegates and Data Sources and Using Delegation to Communicate with Other Controllers may be useful as well.
I'm very new in Mac OS programming. At the moment I'm trying to create simple measurement application which will have one window with the toolbar at the top and the appropriate view in the bottom. Clicking button in the toolbar should result in switching view below it - e.g. clicking on the "Connection" button will show with connection settings, "Measurements" will show current data from the device.
The problem is - I don't know how to handle swapping views, maybe in other words - something I know but not exactly...
I found similar discussion here: NSViewController and multiple subviews from a Nib but there is no answer how to create NSWindowController and how to assign it to the Main window. Because I guess it is necessary to create NSWindowController to be able to swapping views. If I'm wrong, please correct me.
So I'm creating new project (called Sample here) and there is SampleAppDelegate.h file, which looks like:
#interface SampleAppDelegate : NSObject <NSApplicationDelegate> {
#private
NSWindow *window;
}
#property (assign) IBOutlet NSWindow *window;
#end
There is window ivar, which holds the only one window, created from the MainMenu.xib (as I think).
So how should I create NSWindowController for the window from the SampleAppDelegate?
Should I just create my WindowController subclass and in the function
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
of the SampleAppDelegate like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MyWindowController *wc = [[MyWindowController alloc] initWithWindow:self.window];
[wc showWindow:self];
self.myWindowController = wc;
[wc release];
}
I'll be very grateful for any hints and help.
Marcin
You shouldn't need an NSWindowController to do view swapping, NSWindowController used (I think) just when you need multiple toplevel windows.
You can just subclass NSViewController for each type of view that you want, put each view into a nib, and call -(NSView *)view when you need a view to put into the bottom part of the window. You should be able to just add it to the window like normal, or put it in an NSBox by using setContentView:view
For your two views you'd create MeasurmentsViewController and a ConnectionViewController. Then you'd create your views in MeasurementsView.nib and ConnectionView.nib, and use those nibs to initialise your view controllers.
Then in your main window, if you were to put an NSBox, if you wanted to put the MeasurementsView into it
NSView *measurementsView = [measurementsViewController view];
[boxAtBottomOfWindow setContentView:measurementsView];
and to put the ConnectionView into it
NSView *connectionView = [connectionViewController view];
[boxAtBottomOfWindow setContentView:connectionView];