I work on a project for iPhone iOS4 with Xcode 4.
My app uses a tabBar for two Views with two View Controllers.
I want to programmatically create a Button in a View and to have same button in the other view.
For "same button" I mean that buttons have same background Image, same Title and so on. Also, when I programmatically change first button title also second button title change; same for backgrounds.
I was thinking something like "passing the pointer", but I do not know how to do it, how to pass a pointer from a View to another View. (I have a singleton GlobalData, if it can help.)
Thank you.
What you want to do is to create a custom UIButton, and then just use it wherever you need it. Once you change it in it's implementation file it will change globally.
Example CustomButton
//CustomButton.h
#import <UIKit/UIKit.h>
#interface CustomButton : UIButton{
}
#end
//CustomButton.m
#import "CustomButton.h"
#implementation CustomButton
- (id)init
{
self = [super init];
if (self) {
self.type = UIButtonTypeCustom;
self.frame = CGRectMake(170, 45, 150, 40);
[self setTitle:#"Title" forState:UIControlStateNormal];
[self.titleLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:15]];
[self setBackgroundImage:[UIImage imageNamed:#"bg_image.png"] forState:UIControlStateNormal];
}
return self;
}
#end
Then use it like so:
#import "CustomButton.h"
...
CustomButton *myButton = [[CustomButton alloc] init];
Although the approach looks a bit shady, but I do not know what the use cases are so here it goes.
You can create a UIButton subclass and make that a singleton. Or store that in the AppDelegate.
An interesting thing to note is that when you add the same object to a second view, it will be removed from the first view! So you will have to keep adding it back to the view when ViewController's viewWillAppear: method is called.
Related
I want to create a different class with a view and call that class on screen.
When I run the app, the view does not appear. If I delete that structure and create the button on the main file, it works fine. When I put it on a different class, it does not work.
MyView.h
#import <UIKit/UIKit.h>
#interface viewHome : UIViewController
-(UIView*) myHome;
#end
MyView.m (Creating a button for test)
#import "viewHome.h"
#implementation viewHome
-(UIView*) myHome {
UIView * myScreen = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
myScreen.backgroundColor = [UIColor whiteColor];
UIButton * myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
myButton.frame = CGRectMake(100,100,100,44);
[myButton setTitle:#"Login" forState:UIControlStateNormal];
[myScreen addSubview:myButton];
return myScreen;
}
#end
viewController.m
[...]
- (void)viewDidLoad
{
[super viewDidLoad];
viewHome * fncScreen;
UIView * homeScreen = [fncScreen myHome];
[self.view addSubview:homeScreen];
}
Thanks
Its a bad practice to add one viewController's view as a subview to an another viewcontroller. (unless you are using certain classes/methods that let you have childViewControllers).
Its exactly for this reason, your view doesn't appear in one case, and appears in the other case.
try adding the -(UIView*) myHome method in your viewController.m, and do a
[self.view addsubview: [self myHome]];
here's a good SO post about Nested UIViewControllers:
Is it wise to "nest" UIViewControllers inside other UIViewControllers like you would UIViews?
You could call the method [fncScreen myHome] after allocation your view homeScreen.
like this -
UIView *homeScreen = [[UIView alloc] init];
homeScreen = [funScreen myHome];
[self.view addSubView: homeScreen];
[homeScreen release];
Hope this will help you.
One obvious mistake I see in your code is this line:
viewHome * fncScreen;
OK, that's a pointer declaration, but you didn't actually create the object.
You need something like this:
viewHome * fncScreen = [[viewHome alloc] init];
Then you can call methods on such initialized object.
I am fairly new to iOS development. My requirement is I am designing an app that contains 5 screens . I have a set of UI controls( 1 UIImageView 5 UIButtons acting like tab bars for each screen) that are common for all the screens. When a button is clicked only the bottom half of the view needs to change with a relevant details while the buttons stay intact(similar to tab control in windows).
Is there a way to achieve this design? Can I share UI controls across multiple screens without repetition in code or
Is there a way to change only the bottom half of the screen when a button is clicked?
You could have a separate class that would create your UIControls and then for each viewcontroller you call the appropiate method to get the UIControls you want.
#interface UIControlMaker : NSObject{
id controlmakerDelegate; // This is so that you can send messages to the viewcontrollers
}
#property (nonatomic,retain) id controlmakerDelegate; // Release it in dealloc method
- (id)initWithDelegate:(id)delegate;
- (UIView *)createCommonUIControls;
In the implementation file
#implementation UIControlMaker
#synthesize controlmakerDelegate;
- (id)initWithDelegate:(id)delegate{
if(self = [super init]){
[self setControlMakerDelegate:delegate];
return self;
}else
return nil;
}
- (UIView *)createCommonUIControls{
UIView *uicontrolsHolder = [[UIView alloc] initWithFrame:CGRectMake(2,40,320,50)];
// Create as many uicontrols as you want. It'd be better if you have a separate class to create them
// Let's create a button for the menuItem
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0 , 0, 50, 35)];
button.backgroundColor = [UIColor clearColor];
[button setTitle:#"Button 1" forState:UIControlStateNormal];
[button addTarget:controlmakerDelegate action:#selector(buttonOnClick) forControlEvents:UIControlEventTouchUpInside];
[uicontrolsHolder addView:button];
[button release];
// Add more uicontrols here
retun [uicontrolsHolder autorelease];
}
Then in your viewcontrollers create an instance of UIControlMaker and call the createCommonUIControls method which will return a View that you can add to your viewcontroller. Hope that was clear.
I'm looking to create a sizeable subview that's draggable like this:
If there is an IBAction that takes you to the next View (SecondViewController) and when it does, there's another IBAction there and when you click on that one, it creates a SubView that's about half of the size of the current screen you're in (SecondViewController) that shows the third view controller that would be created? Also, how would you make that subView draggable? Thank you for helping.
Sorry, just to be clear, you want your second view controller to have a button that when tapped, adds your third view controller taking up half the screen at the bottom?
If this is the case then you can do this with the new view controller containers in iOS5.
Ok, so you have three view controllers. For the sake of this lets say your class are called FirstViewController, SecondViewController and ThirdViewController.
I assume from what you say that you already have you instance of FirstViewController with a button, that moves you on to an instance of SecondViewController, and that the issue is then getting SecondViewController to add an instance of ThirdViewController to the bottom half of the screen when a button is pressed.
The .m file for SecondViewController will need to do something like this:
#import "ThirdViewController.h"
#interface SecondViewController ()
#property (retain) ThirdViewController *thirdViewConroller;
- (void)buttonTap;
#end
#implementation SecondViewController
#synthesize thirdViewConroller = _thirdViewConroller;
- (void)dealloc {
self.thirdViewConroller = nil;
[super dealloc];
}
- (void)loadView {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.titleLabel.text = #"Show third controller";
[button addTarget:self action:#selector(buttonTap) forControlEvents:UIControlEventTouchUpInside];
button.frame = // Some CGRect of where you want the button to be
[self.view addSubview:button];
}
- (void)buttonTap {
// When the button is tapped, create an instance of your ThirdViewController and add it
self.thirdViewConroller = [[ThirdViewController alloc] initWithFrame:/* Some CGRect where you want the controller to be */ ];
[self.thirdViewConroller willMoveToParentViewController:self];
[self addChildViewController:self.thirdViewConroller];
[self.thirdViewConroller didMoveToParentViewController:self];
}
#end
This should give you a button on your second controller, that will create and add you third controller. Make sure yo us till have all the standard methods that you had before, this should be in addition to what you have.
In your interface for ThirdViewController:
#interface ThirdViewController : UIViewController <NSObject>
- (id)initWithFrame:(CGRect)frame;
#end
Then in the implementation of your ThirdViewController:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.view.frame = frame;
// Do your init stuff here
}
return self;
}
It should then handle adding the views and such forth.
Make sure your thirdViewController class has a valid initWithFrame: initialiser method.
This should do the trick, if you need any further help let me know :)
I was wondering if anyone knew of any good online resources/tutorials for creating views and controllers programatically rather than via the interface builder. Everything I have looked at uses the interface builder and the created nibs, while the IB is ok I would like to have the option of developing these manually (both for practical reasons and get a good understanding of how it all fits together rather than the superficial one you get from dragging and dropping things).
My background is in java and I'm finding it slow and frustrating using the interface builder to develop views the way I would sometimes do them in Java, i.e. either
generate the source programatically from a domain model and then tweak the result if requried
use some meta-data and/or reflection and dynamically add the controls to the view
Also, once I have created a view is there anyway I can add it to the interface builder to make it available to use as a sub view on another view?
Thanks, Vic
The Interface Builder method creates "freeze-dried" objects that are re-created at runtime when you initialize the object from the NIB. It still does the same alloc and init stuff, using NSCoder objects to bring the objects in to memory.
If you want to have a view controller based on a particular NIB, you can then override the default init method and init it based on the NIB for that view controller. For example:
#implementation MyViewController
-(id) init {
if (self = [super initWithNibName:#"MyViewController" bundle:nil]) {
//other setup stuff
}
return self;
}
And when you want to display the MyViewController, you would simply call something like this:
- (void) showMyViewController {
MyViewController *viewController = [[[MyViewController alloc] init] autorelease];
[self presentModalViewController:viewController animated:YES];
}
Now, if you want to create your view manually instead of in Interface Builder, you don't have to change your -showMyViewController method at all. Get rid of your -init override, and instead override the -loadView method of your MyViewController to create it programmatically:
- (void) loadView {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(320,460)];
self.view = view;
[view release];
//Create a button
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton addTarget:self action:#selector(pressedButton) forControlEvents:UIControlEventTouchUpInside];
[myButton setTitle:#"Push Me!" forState:UIControlStateNormal];
myButton.frame = CGRectMake(100,230,80,44);
[self.view addSubview:myButton];
}
This example shows how to create the view and add a button to it. If you want to keep a reference to it, declare it the same way you would if you were using a NIB (without the IBOutlet/IBActions) and use self when assigning it. For example, your header might look like this:
#interface MyViewController : UIViewController {
UIButton *myButton;
}
- (void) pressedButton;
#property (nonatomic, retain) UIButton *myButton;
#end
And your class:
#implementation MyViewController
#synthesize myButton;
- (void) loadView {
//Create the view as above
self.myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton addTarget:self action:#selector(pressedButton) forControlEvents:UIControlEventTouchUpInside];
[myButton setTitle:#"Push Me!" forState:UIControlStateNormal];
myButton.frame = CGRectMake(100,230,80,44);
[self.view addSubview:myButton];
}
- (void) pressedButton {
//Do something interesting here
[[[[UIAlertView alloc] initWithTitle:#"Button Pressed" message:#"You totally just pressed the button" delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK",nil] autorelease] show];
}
- (void) dealloc {
[myButton release];
[super dealloc];
}
I had the same issue a couple of months ago when I wanted to do all the iPhone development inside Emacs. To make a long story short: I'm not developing for the iPhone anymore :)
I'd still suggest you to check my question and some helpful answers here.
I typically don't use Interface builder too much for iPhone development. Usually I will create a view controller in code like this
MyUIViewControllerSubclass *controller = [[MyUIViewControllerSubclass alloc] initWithNibName:nil bundle:nil];
controller.someProperty = myModel;
[self presentModalViewController:controller];
[controller release];
Or something along those lines. Typically I create a subclass of UIViewController and that's where I layout my views and such. The views are subclasses of UIView (either things Apple provides like UIButton etc, or something I've created myself). If you read up on both UIViewController and UIView you should get a pretty good idea of how it works.
Without using Interface builder or xib files, what is the correct way to instantiate two classes which inherit from UIView such that they can switch between themselves using UIButtons located on the views themselves?
I think this involves setting up a UIViewController from the app delegate and adding two instances of my classes which implement UIView into the controller (perhaps from inside the controller?)
I'm also not sure how to raise events from UIButtons on the custom UIViews to switch the views. I suspect I would need to add a method to the view controller but I'm not sure how to get a reference to the view controller from inside the scope of my UIView.
Also, I'm wondering that,if the use of a UIViewController is necessary, should the switch method could be in the scope of the main app delegate?
Some code examples would be great!
Your main problem is that you don't conceptually understand the role of UIViewControllers versus UIViews. Most people don't when they first start out.
Views are stupid and ideally, they should be composed of generic objects. They contain virtually none of the logic of the interface. They do not know or care about the existence of other views. The only logic you put in views is logic that pertains to the immediate and generic functioning of the view itself, regardless of the data it displays or the state of other parts of the app. You seldom need to subclass UIView. This is why views can be completely configured in Interface builder without any code.
ViewControllers contain the logic of the interface and connect the interface to the data (but they do not contain or logically manipulate the data.) They are "intelligent" and highly customized. The viewControllers do understand the place of the view in the context of the app. The viewControllers load and configure the views either from nib or programmatically. The viewControllers control when the views are displayed or hidden and it what order. The viewControllers determine what action is taken in response to events and what data gets displayed where.
VictorB's example code shows how this is all done pragmatically. The important thing to note is that the viewController and view are entirely separate objects from two entirely separate classes. There is no overlap and no need to subclass UIView. All the customization is in the controller.
All this is because of the MVC design patter. It decouples the interface from the data model, making them both modular and independent of each other. This makes it easy to design, debug, and reuse each independent module.
If you want to get it done in code, here is an example I just drummed up using lazy loaded UI elements. I'm only making one button here and swapping it between whichever view is active. It's slightly awkward, but it reduces the amount of code necessary to demonstrate this.
I've created two UIViews to represent your custom classes, one with a blue background and one with a red. The button swaps between the two. If you have a unique button already in each of your custom views, you just need to either expose those buttons as properties of your UIView subclasses so your view controller can access them, or add the view controller as a target for the button's action from within your UIView's loading code.
I've tested this code in my simulator and it seems to work fine, but you really should try to understand what's going on here so you can implement it yourself.
ToggleViewController.h:
#import <UIKit/UIKit.h>
#interface ToggleViewController : UIViewController {
UIView *firstView;
UIView *secondView;
UIButton *button;
}
- (void)addButton;
- (void)toggleViews;
#property (nonatomic, readonly) UIView* firstView;
#property (nonatomic, readonly) UIView* secondView;
#property (nonatomic, readonly) UIButton* button;
#end
ToggleViewController.m:
#import "ToggleViewController.h"
#implementation ToggleViewController
// assign view to view controller
- (void)loadView {
self.view = self.firstView;
}
// make sure button is added when view is shown
- (void)viewWillAppear:(BOOL)animated {
[self addButton];
}
// add the button to the center of the view
- (void)addButton {
[self.view addSubview:self.button];
button.frame = CGRectMake(0,0,150,44);
button.center = self.view.center;
}
// to toggle views, remove button from old view, swap views, then add button again
- (void)toggleViews {
[self.button removeFromSuperview];
self.view = (self.view == self.firstView) ? self.secondView : self.firstView;
[self addButton];
}
// generate first view on access
- (UIView *)firstView {
if (firstView == nil) {
firstView = [[UIView alloc] initWithFrame:CGRectZero];
firstView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
firstView.backgroundColor = [UIColor redColor];
}
return firstView;
}
// generate second view on access
- (UIView *)secondView {
if (secondView == nil) {
secondView = [[UIView alloc] initWithFrame:CGRectZero];
secondView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
secondView.backgroundColor = [UIColor blueColor];
}
return secondView;
}
// generate button on access
- (UIButton *)button {
if (button == nil) {
// create button
button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
// set title
[button setTitle:#"Toggle Views"
forState:UIControlStateNormal];
// set self as a target for the "touch up inside" event of the button
[button addTarget:self
action:#selector(toggleViews)
forControlEvents:UIControlEventTouchUpInside];
}
return button;
}
// clean up
- (void)dealloc {
[button release];
[secondView release];
[firstView release];
[super dealloc];
}
#end
Use Interface Builder. It's there for a reason.