I'm trying to make a subclass of the UIBarButtonItem class. The button is added in the nib file and I set its class to my custom class in the interface builder. Now if this was a UIView class or subclass I would have override the - (id)initWithCoder:(NSCoder *)decoder method to start the extra customization, however UIBarButtonItem lacks such a method. I tried to override its -(id)init method but with no success, it doesn't get called. My question, where should I start my customization? What method do I need to override?
It's because you use IB. When you create an object in IB it does not call the init method for the class, it uses the archive version of the object. So to make custom initializations use this method instead:
-(void)awakeFromNib{
//initialize here
}
Related
In a storyboard when I add a new view (for example a TableView) I can select a class in the "Custom class" field in the identity inspector.
If I understand the rule of this class, I expect this class "answer" to messages sent to my tableview (i.e. this class is my table viewcontroller) and when I run my project it seems to do what I want.
My question is: To do this, I expected my Xcode automatically instantiates an object of my controller class and "link" this object to my GUI in storyboard.
However, I expected that if I override the init method of my controller class with
-(id) init
{
self=[super init];
NSLog(#"object controller created automatically");
return self;
}
I have the string in output when is created my controller object.
Instead, I have no output.
Why is this happenig and what is wrong with the code?
UIView set up by storyboard never called init.
Instead, you should use - (void)awakeFromNib in which your outlet has been ready to use.
- (void)awakeFromNib {
[super awakeFromNib];
NSLog(#"object controller created automatically");
}
From awakeFromNib documentation:
Objects that conform to the NSCoding protocol (including all subclasses of UIView and UIViewController) are initialized using their initWithCoder: method. All objects that do not conform to the NSCoding protocol are initialized using their init method.
If I understand you question you want a message to be printed whenever your viewController is initialised.
Why dont you write the code in the viewDidLoad?
Like:
In your YourControllerClass.m
-(void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Controller created");
}
Now set the class of the controller in the storyboard to YourControllerClassand the message should be printed whenever your controller is created.
Cheers
P.s.: If you still need help or got a question, please write a comment.
I'm new to Objective-C.
I had make a little application with an Image Well (NSImageView) and some Buttons.
To receive the actions from the Buttons and Labels. I created a Class named "Controller". I have connected this class using the "Object" object to the InterfaceBuilder file.
For the Image Well, I created a class to inherits from NSImageView (DImageView) and set this class as class for the ImageWell (using the interfaceBuilder)
In this class I had overwritten the mouseDown Method:
//Class DImageView
- (void) mouseDown:(NSEvent *)theEvent
{
NSLog(#"Test");
}
Now I want to call an method which is defined in the class Controller from this method.
But if I create a new instance of the controller object with [[Controller alloc] init]. I'm creating an new instance and can't access the IBOutlets in the Controller class right?
How can I solve this problem?
Thanks for the help ..
Link it via xib file:
And consider overriding acceptsFirstResponder too, otherwise you will not get any mouse event.
What is the proper way of setting up a separate delegate class for MapKit?
I have MapView class subclassing MKMapView and bare MapDelegate class conforming MKMapViewDelegate protocol having only one initializer method.
Here is the extract from MapView initialization method I use:
# MapView.m ...
#implementation MapView
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// [self setShowsUserLocation:YES];
[self setDelegate:[[MapDelegate alloc] initWithMapView:self]];
The only method MapDelegate class has is
# MapDelegate.m ...
- (id)initWithMapView:(MapView *)aMapView {
self = [super init];
self.mapView = aMapView;
return self;
}
Having [self setShowsUserLocation:YES]; commented, all works fine - I see the map. If I uncomment this line, my application begins to crash.
What my MapDelegate class is missing?
UPDATE 1: if I don't use a separate class MapDelegate and set just setDelegate:self - all works.
UPDATE 2: Now I understand, that the problem with [self setDelegate:[[MapDelegate alloc] initWithMapView:self]]; string is that I need MapDelegate class to live longer than it does now (delegate property has weak attribute). If I do the following:
#property (strong) id delegateContainer;
....
[self setDelegateContainer:[[MapDelegate alloc] init]];
[self setDelegate:self.delegateContainer];
...it works! Is there a better way of retaining MapDelegate life cycle along with the one of MKMapView?
Thanks!
After waiting enough for any answers that could appear here and ensuring original problematic behavior twice more times, I am posting my own answer based on the second update from the question:
The problem with [self setDelegate:[[MapDelegate alloc] initWithMapView:self]]; string is that MapDelegate class should be able to be kept alive outside of the scope of question's initWithFrame method because delegate property has weak attribute. The possible solution is to create an instance variable serving as a container for a delegate class, for example:
#property (strong) id delegateClass;
....
[self setDelegateClass:[[MapDelegate alloc] init]];
[self setDelegate:self.delegateClass];
This solves the original problem.
LATER UPDATE
Though it is possible to set MKMapView's delegate in a separate class, I now realize that such model should not be used:
Currently I always prefer to use my controllers (i.e. controller layer in MVC in general) as delegates for all of my View layer classes (map view, scroll view, text fields): controller level is the place where all the delegates of different views can meet - all situated in controller layer, they can easily interact with each other and share their logic with the general logic of your controller.
On the other hand, if you setup your delegate in a separate class, you will need to take additional steps to connect your separate delegate with some controller, so it could interact with a rest part of your logic - this work have always led me to adding additional and messy pieces of code.
Shortly: do not use separate classes for delegates (at least view classes delegates provided by Apple), use some common places like controllers (fx for views like UIScrollView, MKMapView, UITableView or models like NSURLConnection).
I think viewDidLoad would be a better place to set up the map view. It's just a guess, but perhaps the crash is due to the view not being loaded yet.
Of course subclassing MKMapView isn't recommended at all. You would generally put your map as a subview, and set the main view to be the delegate. From the docs:
Although you should not subclass the MKMapView class itself, you can get information about the map view’s behavior by providing a delegate object.
Finally, if you really want to have a separate delegate class, you don't need to set its mapView, as all delegate methods pass the map as an argument.
does anyone know, which method is called when I set the default title of a UIButton in interface builder?
I have subclassed UIButton and want to provide setting its title easily via interface builder. I assume that it works similar to the User Defined Runtime Attributes which call the setValue: forKeyPath: method.
Edit: So I'm looking for that method which works like "the interface builder tells the button to have that certain title" when instantiating the view.
Thanks in advance!
Interface Builder doesn't call setTitle: method, instead you can override initWithCoder: and awakeFromNib in your UIButton's subclass
- (id) initWithCoder:(NSCoder *)aCoder{
if(self = [super initWithCoder:aCoder){
NSLog([self titleForState:UIControlStateNormal]);
}
return self;
}
The method you're looking for is setTitle:forState:.
I instantiate a class and then I try to change the selector of the instantiated class button:
WebViewController *newtab = [[WebViewController alloc] initWithNibName:#"NavigatorNoBottom" bundle:nil];
[[newtab tabsButton]setAction:#selector(addtabs:)];
The button tabsbutton is an outlet of the WebViewController class which is directly linked in interface builder.
The method -(void)Addtabs:(id)sender is a method in my current class.
But it seems that this code does not work, my button is here but it does nothing when I click on it .
If you need more context don't hesitate. I know this is something maybe very simple but I just bug at it....
Note that -initWithNibName:bundle: does not load the nib file immediately. This means that right after sending it you cannot expect your outlets to be set.
In that line where you send -setAction:, your tabsButton outlet is probably nil.
If WebViewController inherits from NSViewController, you can place that line that sets the action in the -loadView method of your WebViewController class to make sure the nib file has been loaded and all outlets are set:
- (void)loadView {
[super loadView];
[tabsButton setAction:#selector(addtabs:)];
// or [self.tabsButton setAction:#selector(addtabs:)];
}
Alternatively, if you don’t want to do this inside the view controller, you can do the following in an arbitrary class:
WebViewController *newtab = [[WebViewController alloc] initWithNibName:#"NavigatorNoBottom"
bundle:nil];
[newtab view];
[[newtab tabsButton]setAction:#selector(addtabs:)];
By sending -view to the view controller, you force it to load the nib file, hence the tabsButton outlet will have been set right after that.
Note that you can set the action in any class since a selector is not tied to a class. Also note that, since you haven’t set a target, the action will follow the action chain.