Objective-C: Adding UIButtons programmatically from another class - objective-c

I'm having trouble connecting the dots between code and .xib files.
If I want to add a series of UIButtons programmatically to my view controller, how would I go about doing this from a separate class?
For instance, if I have MainViewController.m, which is set as the root view controller in Xcode, how can I add a UIButton to that view controller from SecondViewController.m? Is this even possible?
I would essentially like to place all of my "user interface" code in a separate class.
Thanks

To do this, create a UIButton *myButton programmatically and then call [mainViewController addSubview:myButton];. This may mean you need to store a MainViewController * property in your SecondViewController class.
Important methods and properties for a UIButton instance (essentially, just take a look at the documentation, but here's a minimal set of stuff to get you started):
+[UIButton buttonWithType:buttonType] - Make sure if you're doing anything remotely custom to use UIButtonTypeCustom here (it doesn't give you any default background images or otherwise to have to nil out)
setFrame: - Position the button relative to its container and set the size, for usability reasons the width and height should be at least 44 pixels (as mentioned here).
setTitle:forState: - UIControlStateNormal will act as the default properties for other states too, so you may only need to set the text here
setBackgroundImage:forState: - use UIControlStateNormal and UIControlStateHighlighted/UIControlStateSelected primarily, UIControlStateDisabled if you wish to show it grayed out or inaccessible at any point.
setImage:forState: - Use for an icon next to the button text (like an arrow pointing down for Save or up for Load, etc)
setEnabled:, setHidden:, setSelected: - Transition between different button states. setHighlighted: happens automatically when you tap the button.
addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside - TouchUpInside is almost always what you want for a simple button press, I'm using a method named buttonClicked: here to handle my button press.
Oh, and if you use [[UIButton alloc] initWith...] don't forget to [myButton release] once it's added to the mainViewController :)

use this
#import "MainViewController.h"
#interface SecondViewController
{
MainViewController *mainView;
}
#property(nonatomic, retain) MainViewController *mainView;
-(void)addButtons;
In your implementation
#synthesize mainView;
-(void)addButtons
{
UIButton *add = [UIButton alloc] init];
//do necessary stuff on button here
[self.mainView addSubview:add];
[add release];
}
In your MainViewcontroller.m
#import "SecondViewController.h"
-(void)viewDidLoad
{
[self superViewDidLoad];
SecondViewController *second = [SecondViewController alloc] init];
second.mainView = self;
[second addButton];
[second release];
}

Related

Programmatically create NSPopupButton and add items to list

I've been able to programmatically create a NSPopupButton and add it to my window, and I can add items to the list from the same method, but I'd like to figure out how I can add items to it from another method.
Here's what I have so far that works:
// in my .h file:
#interface AVRecorderDocument : NSDocument
{
#private
NSPopUpButton *button;
}
#property (assign) IBOutlet NSWindow *mainWindow;
// in my .m file:
#implementation AVRecorderDocument
#synthesize mainWindow;
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
NSView *superview = [mainWindow contentView];
NSRect frame = NSMakeRect(10,10,149,22);
NSPopUpButton *button = [[NSPopUpButton alloc] initWithFrame:frame];
[superview addSubview:button];
[button release];
}
- (void)refreshDevices
{
// I'd like to add items to my popupbutton here:
// [button addItemWithTitle: #"Item 1"];
}
#end
Up in refreshDevices I don't get a compiler error, just nothing gets added to the popupbutton. The method refreshDevices is called from -(id)init. I've also tried putting the code that is inside the windowControllerDidLoadNib at the top of my init section, but it won't even create the popupbutton there.
There are a two problems with your code:
Inside windowControllerDidLoadNib:
You don't assign the newly created button to your ivar but only to a function local variable (with the same name as your ivar).
Why nothing happens inside refreshDevices
init is called before windowControllerDidLoadNib:, so your ivar is nil (and because of 1.). Sending messages to nil does nothing.
Solution:
Remove NSPopUpButton * from windowControllerDidLoadNib: so you assign the new button to your ivar and not to some function local variable.
Call refreshDevices at the end of windowControllerDidLoadNib: or at some point you know windowControllerDidLoadNib: has been called and your button is not nil.
Edit:
You should keep in mind that the moment you remove the button from the superview it is probably deallocated because you release it after creation.
The moment it is deallocated your button ivar points to an invalid/deallocated object which leads to undefined behaviour when used in this state.
I'd advise to release the button inside dealloc so you can be sure to have a valid object throughout the whole lifetime of your document object.
But nonetheless I don't know your exact use case which might require this design.

UIView inside a UIViewController or better way to do it?

I have a problem on how to properly do a certain kind of action.
The image below shows a UIViewController, but the second part of the view is a custom UIView (the one with the profile pic, name and Show View button).
The subclassed UIView is allocated using this code:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
[self.view addSubview:profileView];
The problem is of course, that the button on the UIView can't show any view, since it's only the UIViewController that can push another ViewController to the window(correct?).
The UIView is used in several places in the app and needs to be added easily and have the same behavior across the application.
This worked great until I added the button, and I'm starting to think I've made this wrong, and there has to be a better way to do it (maybe change the UIView to something else?).
I was thinking I should be able to call:
self.superview
And then somehow get the ViewController so I can push another ViewController into the view hierarchy, but nope.
Any suggestions and a tips on how to do this correctly?
UPDATE:
I have no idea on how to push another UIViewController from the button.
What should I do in this method when pressing the button in the UIView:
- (void) showViewButtonTouched:(UIButton*)sender {
GPProfileSocialFriendsViewController *friendsSettings = [[GPProfileSocialFriendsViewController alloc] init];
}
How do I push GPProfileSocialFriendsViewController?
Your - (void) showViewButtonTouched:(UIButton*)sender method should be in your controller and would probably be better named - (void) showView:(UIButton*)sender or - (void) showProfile:(UIButton*)sender so it clearly denotes what it does (not how you got there).
It's not the view's responsibility to manage transitions from a state to another. If you move your method to your controller, your problem is no more (you can easily access self.navigationController or push directly if you don't have an navigation controller like this:
[self presentViewController:vcThatNeedsToBePushed animated:YES completion:nil];
I think you can create weak reference in GPProfileView on UIViewController. Like this:
#property (weak, nonatomic) UIViewController *rootController;
when you create GPProfileView, assign rootController-property:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
profileView.rootController = self; // if view created in view controller
[self.view addSubview:profileView];
and in implementation of button selector:
self.rootController push... // or pop
May be this not correct, but you can try
You could let the view controller push the next view controller when the button is pushed. The view controller can add a target/action on the button, so that the action method in the view controller is called on the touch up inside event.

Correctly link my buttons to my views (in UINavigationController)?

I'm sorry if this question sounds kinda newbie, but I'm a beginner and want to improve. So, I have a Navigation Controller embedding my whole application. My main view has a button, linking to another view.
Here is what I did in my main view:
.h
#property (nonatomic, retain) IBOutlet UIButton *button;
In .m, some code for my button.
In IB, I've added a button in my main view and another ViewController whose I changed the class. And the following links :
button -> Button
button -> OtherViewController (Push)
Now my question is, what do I miss to add in my code or IB ? Do I need an IBAction too ?
Thanks a lot for your advices..
-(IBAction)clickButton:(id)sender{
if (!createViewController) {
createViewController = [[CreateViewController alloc] initWithNibName:#"CreateViewController" bundle:nil];
}
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backBarButtonItem;
[backBarButtonItem release];
[self.navigationController pushViewController:createViewController animated:YES];
}
Yes, you need a method that defines the action for your button.
In .h:
- (IBAction)buttonPressed:(id)sender;
In .m:
- (IBAction)buttonPressed:(id)sender { // what you want to do when the button is pressed }
Then click on the button from your interface, from Connection Inspector, control click the radio button from "Touch Up Inside", connect to File's Owner and select the method -buttonPressed.

TextField will NOT resign first responder with UIModalPresentationFormSheet view

I've created a button on one viewController that loads another view modally using the UIModalPresentationFormSheet presentation style. On this loaded view, I have two textFields, and I'm forcing the first textField to be first responder so that the keyboard will appear immediately with the new view. I've set up the textFields to have an action method that is hooked up to "Did End on Exit" event. However, whenever I hit "return" on the keyboard for either textField, the keyboard fails to go away (Here is my code):
// addCustomPage method that is called when button from original view is touched
- (IBAction) addCustomPage:(id)sender
{
NSLog(#"Adding Custom Page");
if (!self.customPageViewController)
{
self.customPageViewController =
[[CustomPageViewController alloc] initWithNibName:#"CustomPageViewController" bundle: nil];
}
customPageViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:customPageViewController animated:YES];
// force keyboard to appear with loaded page on the first textField
[customPageViewController.firstTextField becomeFirstResponder];
}
#interface CustomPageViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *firstTextField;
#property (strong, nonatomic) IBOutlet UITextField *secondTextField;
- (IBAction)keyboardEndOnExit:(id)sender; // DID END ON EXIT EVENT
#end
//in CustomPageViewController.m
-(IBAction)keyboardEndOnExit:(id)sender
{
[sender resignFirstResponder];
}
This is a fairly straight forward problem, and I have no problem normally dismissing keyboards using this technique with basic views and textFields. I'm not sure if using a view in this presentation format, or set up makes things different. Thanks!
You have confirmed that you keyboardEndOnExit method is actually being called?
You could also take a more direct approach by calling [yourTextView resignFirstResponder] when a specific action is take by the user, such as a key pressed etc. I would still check if that method is ever being called using breakpoints or a log.
Have a look at this question. Pretty sure it is the same problem caused by UIModalPresentationFormSheet.

Append text with UITextField text

I am running Objective-C/Cocoa in Shell in Windows, not on Mac OS X.
I want to append a string with an existing string in a text field when I click a button. I've created a button in a window, but I don't know how to append a string when I click that button.
How can I append a string to a text field when that button is clicked?
This is a pretty empty question and will probably get deleted quickly. However, to attempt to answer it in some way:
If you want to display something when you click a button then you'll need a UIButton (already added you have said) and let's say a UILabel. You can find a UILabel in the same window you discovered the button. Add it to the view, somewhere just below the button.
You then want to find the .h file that corresponds to the .xib file (if you set something up by default in XCode it will have been created for you). Let's call it MyViewController.h although it will have a different name in your app.
In that .h file, you want to create an IBAction (so that you can detect the button click in code) and an IBOutlet (so you can update the text on the label).
The code should end up looking something like this:
//
// MyViewController.h
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController {
UILabel *label;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
- (IBAction)buttonClick;
#end
Then, go back to the .xib file where you created your button. Select "File's Owner", open up the "Identity inspector" under the "Tools" menu and make sure the class is set to the same name as the class whose .h file we were just editing.
Now we need to connect the button and the label. Click on the button and bring up the "connections inspector" from the "Tools" menu again. Click in the circle next to "touch up inside" and drag it to the "File's owner". Select the "buttonClick" method that pops up in a window for you. Now select the File's owner. Click in the circle next to the word "label" and drag it to the label on your iPhone screen. This has now connected up the label and the button to the code you wrote before.
Finally, save the .xib file and open up the MyViewController.m file (or whatever it is called in your app). Add the following to it:
//
// MyViewController.m
#import "MyViewController.h" // Swap this name for the name of your .h file
#implementation MyViewController
#synthesize label;
-(IBAction)buttonClick {
label.text = #"Hello World!"
}
- (void)dealloc {
[label release];
[super dealloc];
}
#end
Spend some time looking at various tutorials etc and you'll quickly get up to speed.
You didn't specify whether you're using UILabel (Cocoa Touch) or NSTextField (Cocoa). For the latter, get the field's stringValue, send that object a stringByAppendingString: message with the string you want to append, and pass the result to the field's setStringValue: method.
Short answer: create an IBAction method in a controller class and set the IBAction as the target of your button.
Long answer: Get a book and learn the details of Cocoa. Cocoa is difficult to get started with, but once you've made the initial investment you'll be able to quickly create apps. I'd recommend Cocoa Programming for Mac OS X by Aaron Hillegass. It's desktop orientated rather than iPhone, but that shouldn't put you off.
There are also loads of great web resource to learn Cocoa. Cocoa Dev Central and the Apple developer site are great places to start.
import <UIKit/UiKit.h>
#interface MyViewController : UIViewController {
    UIButton *btnToAppend;
UITextField *textfield;
}
- (IBAction)buttonClick:(id)sender;
#end
import "MyViewController.h"
#implementation MyViewController
-(void)init{
btnToAppend = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btbToAppend.frame = CGRectMake(10, 10, 100, 30);
[btnToAppend setText:#"Append Text" forState:UIControlStateNormal];
[btnToAppend addTarget:self action:#select(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubView:btnToAppend];
textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 200, 300, 50)];
textfield.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubView:textfield];
}
-(IBAction)buttonClick:(id)sender {
NSString *string = [textfield.text stringByAppendingString:#"Welcome "];
    textfield.text = string;
}
- (void)dealloc {
    [super dealloc];
}
#end
Output will be like :
Welcome Welcome Welcome ...