pressing UIBarButtonItem in UINavBar gives unrecognized selector sent to instance - objective-c

I have a view that implements a splitView controller.
I had to add TWO buttons to the Navigation controller. I added the first in UIBuilder but it would not let me add a second one so I did it in code.....
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview: _splitViewController.view];
_splitViewController.delegate = _rightViewController;
_leftViewController.delegate = _rightViewController;
_rightViewController.leftView = _leftViewController;
bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done)];
_navItem.leftBarButtonItem = bbi;
}
Where bbi was declared in the header as
#property (nonatomic, retain) UIBarButtonItem * bbi;
with the matching definition above and synthesis.
I have declared the done method as
- (void)done:(UIBarButtonItem *)sender;
but when I click the done button I get the error
-[ConfigurationViewController done]: unrecognized selector sent to instance
I have tried a number of things, with bbi declared locally instead of in the h file,
with done not being declared in the h file, having the done method expect an id instead, but no luck.
I am obviously doing something stupid. Any idea what?

I think done should have a colon after it if it is a method like this:
bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done:)];

Related

Add UINavigationItem to UINavigationController that wraps Google Drive OAuth

Goal: For connection to iOS Google Drive, wrap the iOS Google OAuth view controller in a programmatically created navigation controller, and add a Cancel button to enable the user to cancel the Google OAuth process, should they choose to do so.
Problem: While I can successfully wrap the OAuth view controller in a navigation controller, I cannot seem to add a navigation item, such as the desired Cancel button.
I add a navigation controller that wraps the Google Drive OAuth view controller, as follows...
GTMOAuth2ViewControllerTouch *authViewController = nil;
if (!self.isAuthorized) {
SEL selectorFinish = #selector(viewController:finishedWithAuth:error:);
SEL selectorButtonCancel = #selector(buttonCancelTapped:);
authViewController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:kGTLAuthScopeDrive
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:selectorFinish];
UINavigationController *navController = [[UINavigationController alloc] init];
[navController addChildViewController:authViewController];
[self.parentTVC presentViewController:navController animated:YES completion:nil];
}
For clarity, the variable parentTVC is a public property,
#property (nonatomic, strong) UITableViewController *parentTVC;
and is set using a custom init method, as follows...
- (id)initWithParentTVC:(UITableViewController *)tvc {
self = [super init];
[self setParentTVC:tvc];
return self;
}
I have attempted to add UINavigationItems to the UINavigationController instance navController, however this does not work, and instead I seem to be stuck with the UIView with the two small buttons (< and >) in the nib file GTMOAuth2ViewTouch.xib, image included below...
I have read through the GTL file GTMOAuth2ViewControllerTouch.m to attempt to see whether there is a method I could possible use or whether I can override by subclassing, but I am not confident in my attempts to do this.
My best guess is that any navigation controller wrapping the OAuth view controller set by this code from GTMOAuth2ViewControllerTouch.m...
- (void)setUpNavigation {
rightBarButtonItem_.customView = navButtonsView_;
self.navigationItem.rightBarButtonItem = rightBarButtonItem_;
}
Assistance please?
This is my re-interpretation of Imran Khan's excellent answer provided in his response to this stack overflow question: Google Drive iOS SDK: Display Cancel Login Button
The googleAuthCheck method should be called in either the viewDidLoad or viewWillAppear method of the parent view controller. (I assume here a reasonable understanding of iOS Google Drive SDK, so let me know if I need to add further clarification.)
Also, albeit a small issue, using initWithBarButtonSystemItem:UIBarButtonSystemItemCancel requires that only the title text of the view controller then needs to be localised (if you are implementing localisation).
- (void)googleAuthCheck {
if (!self.isAuthorized) {
SEL selectorFinish = #selector(viewController:finishedWithAuth:error:);
SEL selectorButtonCancel = #selector(buttonCancelTapped:);
UINavigationController *navController = [[UINavigationController alloc] init];
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:<<localised string for title>>];
UIBarButtonItem *barButtonItemCancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:selectorButtonCancel];
UINavigationBar *navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 320, 63)];
[navigationItem setRightBarButtonItem:barButtonItemCancel];
[navigationBar setTranslucent:NO];
[navigationBar setItems:[NSArray arrayWithObjects: navigationItem,nil]];
[navController.view addSubview:navigationBar];
GTMOAuth2ViewControllerTouch *authViewController = nil;
authViewController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:kGTLAuthScopeDrive
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:selectorFinish];
[navController addChildViewController:authViewController];
[self.parentTVC presentViewController:navController animated:YES completion:nil];
}
}
For clarity, the buttonCancelTapped: method is as follows...
- (IBAction)buttonCancelTapped:(UIBarButtonItem *)sender {
[self.parentTVC dismissViewControllerAnimated:YES completion:^(void){}];
}
For clarity, the variable parentTVC is a public property,
#property (nonatomic, strong) UITableViewController *parentTVC;
and is set using a custom init method, as follows...
- (id)initWithParentTVC:(UITableViewController *)tvc {
self = [super init];
[self setParentTVC:tvc];
return self;
}
This custom init method is called from the parent view controller.

cant figure out how to change views using an NSTimer

I made a class called view2 which is a UIView Controller subclass and when i run the program the timer works fine but when it changes screens it just goes to black and not the picture I have on view2. This is the code I have on the .m of the opening view can anyone see where this is going wrong?
- (void)viewDidLoad
{
randomMain = [NSTimer scheduledTimerWithTimeInterval:(2) target:self selector:#selector(onTimer) userInfo:nil repeats:NO];
[super viewDidLoad];
}
-(void)onTimer{
view2 *second= [[view2 alloc] initWithNibName:nil bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
You have to specify the name of the xib file you want to load (without extension):
view2 *second = [[view2 alloc] initWithNibName:#"MyView2Xib" bundle:nil];
Or if you create the GUI programmatically, just use a simple init:
view2 *second = [[view2 alloc] init];
Some additional remarks:
Class names should always start with a capital letter, to distinguish them from variables.
It's a good idea to include part of the superclass' name in your subclass' name. If I saw a class called View2, I would assume it's a UIView subclass. I'd rather call it ViewController2.
There is no need to put the interval in parenthesis.
The action of an NSTimer should have a single argument (itself).
EDIT:
With a storyboard you have to manually perform the seque you set up between the 2 views:
- (void)onTimer {
[self performSegueWithIdentifier:#"YourSegueIdentifier" sender:self];
}
I would think viewDidLoad is not the appropriate place for your purposes. You should instead use viewDidAppear.
All of DrummerB's remarks are very valid and quite important.

Create subview on awakeFromNib

I'm trying to create a NSImageView programmatically as a subview of another NSImageView when awakeFromNib is called.
My code is as follows (Fader is defined in MyImageView.h):
#implementation MyImageView
- (void)awakeFromNib {
Fader = [NSImageView initWithFrame: [self frame]];
}
I get the warning message "NSImageView may not respong to +initWithFrame". When I build, the app simply frizzes without showing anything, and I have to "force quit".
What am I doing wrong?
You’ve forgotten to send +alloc in order to allocate the object. Change that line to:
Fader = [[NSImageView alloc] initWithFrame: [self frame]];

Working on custom component: subclass UIView or UIViewController?

I'm working on a custom implementation of UISegmentedControl.
I'd like to create a component that able to receive config data and from which obtain a custom View similar to UISegmentedControl.
I started subclassing a UIView and i can create a custom UISegmentedControl with this code:
CustomSegment *segment = [[CustomSegment alloc]
initWithTitles:[NSArray arrayWithObjects:#"one",#"two",nil]];
[self.window addSubview:segment];
But now i'd like to improve my class and add some more customizable parameters to it.
For example i'd like add a custom separators, define the button fonts and so on... here my doubt:
Is it better to work on a UIView subClass or you suggest me to subclass a UIViewController, where i can manage View hierarchy in method like -(void)loadView and -(void)viewDidLoad ?
In a simple UIView subclass, when i launch the custom init method, i setup immediately subviews... while using a UIViewController i can call custom init and define how my subview is builded into -(void)loadView.
Don't use an UIViewController, just extend the UIView class like you did and keep extending its functionality.
Remember to save a pointer to each subview you add (i.e. buttons) in order to be able to access them later.
Define custom setters, for example, a custom setter for changing a button label title would be:
- (void) setButton1Title:(NSString*)str forState:(UIControlState)state{
//You can add some control here
if ([str length] > 20) return;
[_button1 setTitle:str forState:state]; //_button1 is my reference to the button
}
And so on. Don't provide direct access to your subviews, use methods instead.
Also, you can use "layoutSubviews" method to define how your views are going to be displayed in your custom view.
Hope it helps you.
Edit: In your case, I don't see why using lauoutSubviews method but I want to show you what I was trying to say.
Lets say that for example I need to create an UIView class to represent a "Contact" object in my application.
This is what I would do:
#interface ContactView : UIView{
UILabel* _nameLabel;
UILabel* _ageLabel;
Contact* _contact;
}
#property (retain) Contact* contact;
#end
#implementation ContactView
#synthetize contact = _contact;
-(id)initWithContact:(Contact*)c{
self = [super init];
if (self) {
_nameLabel = [[UILabel alloc] init];
_nameLabel.frame = CGRectZero;
[self addSubview:_nameLabel];
[_nameLabel release];
_ageLabel = [[UILabel alloc] init];
_ageLabel.frame = CGRectZero;
[self addSubview:_ageLabel];
[_ageLabel release];
self.contact = c;
}
}
- (void) layoutSubviews{
[super layoutSubviews];
_nameLabel.frame = CGRectMake(0.0f, 0.0f, 200.0f, 25.0f);
_ageLabel.frame = CGRectMake(0.0f, 25.0f, 200.0f, 25.0f);
if (self.contact){
_nameLabel.text = self.contact.name;
_ageLabel.text = self.contact.age;
}else{
_nameLabel.text = #"Unavailable";
_ageLabel.text = #"Unavailable";
}
}
- (void) setContact:(Contact*)c{
self.contact = c;
[self layoutSubviews];
}
#end
Check out how the "layoutSubiews" is used to set the correct frame and data to the labels.
Usually, I use it a lot when creating custom UITableViewCells where you have to reuse the view.
Let me know if I'm being confusing.

Setting a ViewController's properties after instantiation

I'm creating an instance of a viewController, and then trying to set the text on of it's properties, a UILabel.
BoyController *boyViewController = [[BoyController alloc] initWithNibName:#"BoyView" bundle:nil];
NSString *newText = [astrology getSignWithMonth:month withDay:day];
boyViewController.sign.text = newText;
NSLog(#" the boyviewcontroller.sign.text is now set to: %#", boyViewController.sign.text);
[newText release];
I tried this, but it didn't work...
So I tried the following:
BoyController *boyViewController = [[BoyController alloc] initWithNibName:#"BoyView" bundle:nil];
UILabel *newUILabel = [[UILabel alloc] init];
newUILabel.text = [astrology getSignWithMonth:month withDay:day];
boyViewController.sign = newUILabel;
NSLog(#" the boyviewcontroller.sign.text is now set to: %#", newUILabel.text);
[newUILabel release];
But no avail..
I'm not sure why I can't set the text property of the UILabel "sign" in boyViewController..
The problem here is that the initializer does not actually load the nib file into memory. Instead, loading the nib is delayed until your application requests the view controller's view property. As such, your controller's sign property is null when you access it.
Manually requesting the controller's view property would make your example work...
BoyController *boyViewController = [[BoyController alloc] initWithNibName:#"BoyView" bundle:nil];
[boyViewController view]; // !!!: Calling [... view] here forces the nib to load.
NSString *newText = [astrology getSignWithMonth:month withDay:day];
boyViewController.sign.text = newText;
// and so on...
However, I'd guess that what you're really trying to do is create and configure your view controller before setting it free to do it's own thing. (Perhaps to display it modally, say.) Calling [... view] manually is not going to be a long-term solution.
Better is to set a separate property on your view controller for the label text and then implement viewDidLoad to assign it to the label:
#interface BoyViewController : UIViewController {
IBOutlet UILabel *label;
NSString *labelText;
}
#property(nonatomic, copy)NSString *labelText;
#end
#implementation
#synthesize labelText;
- (void)viewDidLoad
{
[label setText:[self labelText]];
}
// and so on...
#end
This has the added benefit of your label text being reset in case the view is purged during a low memory event.
Did you bind your outlets at Interface Builder?
It seems that you need to bind sign outlet of the first example into Interface Builder in order to actually set that text to whatever you want.
Once you bind your outlet to the actual UI component at Interface Builder, then you should be able to do something like:
NSString *newText = [astrology getSignWithMonth:month withDay:day];
[[boyViewController sign] setText:newText];
This is what you need to know about binding.
Your second example does not make sense at all to me.