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.
Related
I have a UIViewController which I want to display a UIView that renders as a menu. This menu will have several buttons on it. I wanted to reuse this menu a few different places in my app so I figured I would create a class called ViewFactory that has a method that returns a UIView with these buttons.
On my ViewController I call this method and get the returned UIView and add it as a subview.
This works just fine. I can see the view and all its buttons, however, the buttons do not respond to any touch events. Not sure why this is the case and curious to know what I am doing wrong.
Here is my code for the ViewFactoryClass:
- (UIView *) addCloseRow
{
// UIView container for everything else.
UIView *navRow = [[UIView alloc] initWithFrame:CGRectMake(0,225,350,45)];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.userInteractionEnabled = YES;
[navRow addSubview:button];
[button addTarget:self action:#selector(closeButtonTouchDownEvent) forControlEvents: UIControlEventTouchDown];
navRow.userInteractionEnabled = YES;
return navRow;
}
In my main NavigationController class here is how I am calling and getting the UIView:
ViewFactory *factory = [[ViewFactory alloc] init];
[self.navigationController.navigationBar addSubview:[factory MainNavigationUIView]];
Again, the UIView shows up but the buttons never respond to anything.
You added the button with target and selector for ViewFactoryClass
And now you are creating instance and trying to call an action from ViewFactory class.
You can change the method to something like this:
- (UIView *) addCloseRow : (id)object {
...
[button addTarget:[object class] action:#selector(closeButtonTouchDownEvent) forControlEvents: UIControlEventTouchDown];
...
}
I've been following this example in everything to create a UIWindow on top of the statusBar.
My UIWindow gets displayed on top of the statusBar and all is fine, but the actual view of the app (the one with the button) doesn't respond to my actions:
I'm using Storyboards and iOS6.
Here's my code for creating a statusBar overlay:
- (void)viewDidAppear:(BOOL)animated {
UIWindow *overlayWindow = [[ACStatusBarOverlayWindow alloc] initWithFrame:CGRectZero];
AppDelegate *app = [[UIApplication sharedApplication] delegate];
overlayWindow.rootViewController = app.window.rootViewController;
app.window = overlayWindow;
[overlayWindow makeKeyAndVisible];
}
The view under the statusBar does not respond and I can't tap on the UIButton. Is it possible to somehow make the UIWindow with the interface of my app accept the touches ignoring the ACStatusBarOverlayWindow? How can that be done?
Usually if a button does not respond to a touch it's because the button is outside of the bounds of it's parent's UIView.
Your code does not seem to be the appropriate approach to the problem you're trying to solve. If you just need your window to have a status bar, or you just need to add a button to you current view, the way you're doing it is probably incorrect.
Personally I've never seen anyone instantiate a UIWindow in a viewDidAppear, since the app comes with it's own UIWindow. You should be using a UIView and adding your overlay to it.
As a side note if you were to do it the way you're attempting to, then your window would at least need a frame. So initWithFrame:CGRectZero would be initWithFrame:CGRectMake(0,0,320,480) or something along those lines.
A better way to approach the problem is to instantiate a UIViewController and set it as your rootViewController. Or simply add your button to the current viewController's view.
- (void)viewDidLoad {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(myMethod:)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"Tap Me" forState:UIControlStateNormal];
[button sizeToFit];
[self.view addSubview:button];
}
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.
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.