UIButton and UIPicker - iphone

Is there any reason adding a UIPicker to a view might somehow rob a programatically created button of its functionality? I initially instantiated a UIButton to take me to a different view, and all was well and fine. Then I went ahead and added a UIPicker to the same view with the purpose of selecting a few key fields which could then be passed on to the view the UIButton led to. Unfortunately it seems I somehow broke my button in the process of adding the picker.
Here's the instantiation of the button:
switchToGame = [UIButton buttonWithType:UIButtonTypeRoundedRect];
switchToGame.frame = CGRectMake(140,250,100,100);
[switchToGame setTitle:#"Play God" forState:UIControlStateNormal];
[switchToGame setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[switchToGame addTarget:self action:#selector(playGame) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:switchToGame atIndex:0];
The method playGame checks the fields of the picker, sends them to a shared singleton object then loads the game view as such:
Singleton *mySingleton = [Singleton sharedSingleton];
NSInteger numCellsRow = [theDataPicker selectedRowInComponent:0];
NSInteger aliveColorRow = [theDataPicker selectedRowInComponent:1];
NSInteger deadColorRow = [theDataPicker selectedRowInComponent:2];
UIColor * theAliveColor = [aliveColors objectAtIndex:aliveColorRow];
UIColor * theDeadColor = [deadColors objectAtIndex:deadColorRow];
NSInteger numCellsPerRow = [[cellRowOptions objectAtIndex:numCellsRow] intValue];
mySingleton.cellsPerRow = numCellsPerRow;
mySingleton.aliveColor = theAliveColor;
mySingleton.deadColor = theDeadColor;
[theAliveColor release];
[theDeadColor release];
GameController * viewController = [GameController alloc];
self.theViewController = viewController;
[self.view insertSubview:theViewController.view atIndex:1];
[viewController release];
I hope these stretches of code are enough for someone to point out where I went astray, as I am quite lost at the moment.

Out of curiosity what happens if you replace:
[self.view insertSubview:switchToGame atIndex:0];
with
[self.view insertSubview:switchToGame atIndex:1];
and
[self.view insertSubview:theViewController.view atIndex:1];
with
[self.view insertSubview:theViewController.view atIndex:0];
Hopefully this might order your views correctly so that the button is on top. Also look here for a great link on addSubview versus insertSubview:
Difference between addSubview and insertSubview in UIView class

Related

UIButton never responds within a UIImageView's frame area

Here's my view hierarchy: parentView (UIView) has a UIImageView as its subview which in turn has a UIButton as its subview.
left = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"left.png"]];
left.userInteractionEnabled = YES;
[parentView addSubview:left];
back = [UIButton buttonWithType:UIButtonTypeCustom];
[back setImage:[UIImage imageNamed:#"Arrow-left.png"] forState:UIControlStateNormal];
[back addTarget:self action:#selector(back:) forControlEvents:UIControlEventTouchUpInside];
back.showsTouchWhenHighlighted = YES;
[left addSubview:back];
Everything shows up properly but the button does not respond to touches. It does respond if I move its frame out of the UIImageView's frame to somewhere else and set it as a subView to the parentView (UIView). But here's the thing.
Even if I set to parentView's subView the button does not respond if it is within the UIImageView's frame area. The userInteractionEnabled property has been already set to YES for the image view. Any idea what's going on?
UIImageView turns off userInteraction - turn it on and the button will work.
EDIT:
So I used your code almost exactly as written - the one red herring is that you said it all appears fine. For me, the custom button had a frame of 0,0,0,0 so I saw nothing. When I set the frame it all worked perfectly:
UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [UIImage imageNamed:#"46-truck.png"];
assert(image);
[back setImage:image forState:UIControlStateNormal];
[back addTarget:self action:#selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
back.showsTouchWhenHighlighted = YES;
back.frame = (CGRect){ {0,0}, image.size};
NSLog(#"FRAME: %#", NSStringFromCGRect(back.frame) );
[imageView addSubview:back];
So, if you need to probe the superviews during run time to figure out what is what, you can use this code below. [UIView dumpSuperviews:back msg:#"Darn Bark Button"];
#interface UIView (Utilities_Private)
+ (void)appendView:(UIView *)v toStr:(NSMutableString *)str;
#end
#implementation UIView (Utilities_Private)
+ (void)appendView:(UIView *)a toStr:(NSMutableString *)str
{
[str appendFormat:#" %#: frame=%# bounds=%# layerFrame=%# tag=%d userInteraction=%d alpha=%f hidden=%d\n",
NSStringFromClass([a class]),
NSStringFromCGRect(a.frame),
NSStringFromCGRect(a.bounds),
NSStringFromCGRect(a.layer.frame),
a.tag,
a.userInteractionEnabled,
a.alpha,
a.isHidden
];
}
#end
#implementation UIView (Utilities)
+ (void)dumpSuperviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
while(v) {
[self appendView:v toStr:str];
v = v.superview;
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
+ (void)dumpSubviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
if(v) [self appendView:v toStr:str];
for(UIView *a in v.subviews) {
[self appendView:a toStr:str];
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
#end
Maybe
parentView.userInteractionEnabled = YES;
Or parent view frame are to small and can`t hold button or image view. Check size of parent view;
I've had this happen before.. I think it was a super view swallowing the event.
I know you've stated that adding it to the parentView only works if it doesn't overlap with the image view. If you disable user interaction on the image view, does the button event trigger consistently when added to the parentView?
You actually need to set userInteractionEnabled = NO to allow the touches to pass through.
Taken from here.
left.userInteractionEnabled = YES;
put this line in the end.

Method to create UIButtons

this is my first post, any help would be greatly appreciated.
I have to create a number of UIButtons programmatically, which I have done, but the code gets quite messy when there are alot of buttons so I have written a method that will take a UIbutton, string and UIcolor as input and set the rest of the UIButton attributes for me. The problem is the buttons don't seem to be created. Is what I'm trying possible or am I going about it the wrong way.
The method
-(void)makeButton:(UIButton *)name titleOfButton:(NSString *)title buttonColor:(UIColor *)color {
name =[[UIButton alloc] initWithFrame:(CGRectMake(400,400,150,100))];
[name setAlpha:(0.5)];
[name setBackgroundColor: color];
[name setTitle:title forState:UIControlStateNormal];
name.titleLabel.font = [UIFont systemFontOfSize:16];
name.titleLabel. numberOfLines = 0; // Dynamic number of lines
name.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
name.titleLabel.textColor = [UIColor blackColor];
[myView addSubview:name];
}
and to call the method
[self makeButton:JDLabel
titleOfButton:#"JD"
buttonColor:[UIColor redColor]];
The method has been declared in the header file and the buttons at the top of the class.
This is some code to get you started. This will work if you paste it to your view controller, generally put it in the view you want but change "self.view" to "self" or any view you want as long as you have the reference to it. Just call "addSomeButtons" on "viewDidLoad" or as some target..
- (void)buttonPressed:(UIButton *)sender {
NSLog(#"button %d pressed", sender.tag+1);
}
- (void)makeButtonWithtitle:(NSString *)title andColor:(UIColor *)color atPositionIndex:(NSInteger)index {
const CGFloat buttonHeight = 60.0f;
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(.0f, buttonHeight*index, 300.0f, buttonHeight)];
button.tag = index;
[button setAlpha:(0.5)];
[button setBackgroundColor: color];
[button setTitle:title forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:16];
button.titleLabel. numberOfLines = 0; // Dynamic number of lines
button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
button.titleLabel.textColor = [UIColor blackColor];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
[button release];
}
- (void)addSomeButtons {
for(int i=0; i<10; i++) {
[self makeButtonWithtitle:[NSString stringWithFormat:#"button %d", i+1] andColor:[UIColor greenColor] atPositionIndex:i];
}
}
Anyway what you are trying to do is very common and very possible. For instance I never ever ever use the interface builder.
set the different tag value of to separate the button from another button like:
button.tag = 1;
here you should use variable like
in .h
int i ;
in .m
button.tag = i;
and set the frame size of one button to another button
UIButton *name = [UIButton buttonWithType:UIButtonTypeRoundRectangle];
name.frame = CGRectMake(10*i,10*i,150,100);
i++;
UIButtons are getting created but getting overlapped over each other.. u just need to change the frame with every method call too.. 400,400 will create the button off screen .. i don't think thats what u want .. try something like 160,240
You might want to consider creating the Buttons in Interface Builder, which I find much more convenient for setting the size, colors etc.
You can then reference the buttons in the code via either with an IBOutlet per Button or with an IBOutletCollection, which let's you "group" a number of UIViews in one Outlet:
Simply create your UIButtons in Interfacebuilder and place them next to your main view, so that they won't be displayed initially.
CMD + Click all buttons and CTRL + Drag to your .h file and create an IBOutletCollection which will eventually look like this in your code:
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *allMyButtonsFromIB;
Now you can access the buttons in the implementation via self.allMyButtons, which is an NSArray
-(void)createButtonWithTitle:(NSString*)title andColor:(UIColor*)color andFrame:(CGRect)frame{
UIButton *lButton = [UIButton buttonWithType:UIButtonTypeCustom];
[lButton setFrame:frame];
[lButton setTitle:title forState:UIControlStateNormal];
lButton.titleLabel.font = [UIFont systemFontOfSize:16];
[lButton setBackgroundColor:color];
[self.view addSubview:lButton];
lButton = nil;
}
You can change the frame values to put the buttons at appropriate positions on the view.

Dealing with views and some other things

I'm study iOS development. Now playing with views. I don't use interface builder at the moment, because I prefer to understand how all stuffs works under the hood. This is why I create my views UI elements etc programmatically. However.
Here is how my project looks like.
I have a class called RootViewController. I use that class as a window rootViewController.
Within that class I have a logic which load/unload views. Here is some code.
RootViewController.m
-(void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.view = view;
self.view.backgroundColor = [UIColor lightGrayColor];
// create FirstViewController button
UIButton *firstViewButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
firstViewButton.frame = CGRectMake(20, 60, 280, 50);
firstViewButton.tag = 1;
[firstViewButton setTitle:#"Load First view" forState:UIControlStateNormal];
[firstViewButton addTarget:self
action:#selector(loadViewControlllers:)
forControlEvents:UIControlEventTouchUpInside];
// crate SecondViewController button
UIButton *secondViewButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
secondViewButton.frame = CGRectMake(20, 140, 280, 50);
secondViewButton.tag = 2;
[secondViewButton setTitle:#"Load Second view" forState:UIControlStateNormal];
[secondViewButton addTarget:self
action:#selector(loadViewControlllers:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:firstViewButton];
[self.view addSubview:secondViewButton];
[self.view release];
}
-(void)loadViewControlllers:(UIButton *)sender
{
if ([sender tag] == 1) {
if(firstViewController == nil) {
firstViewController = [[FirstViewController alloc]
initWithNibName:#"FirstViewController"
bundle:nil];
[self.view addSubview:firstViewController.view];
}
} else {
if(secondViewController == nil) {
secondViewController = [[SecondViewController alloc]
initWithNibName:#"SecondViewController"
bundle:nil];
[self.view addSubview:secondViewController.view];
}
}
}
The first and the second view controllers contain same code
-(void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.view = view;
self.view.backgroundColor = [UIColor redColor];
UIButton *remove = [UIButton buttonWithType:UIButtonTypeRoundedRect];
remove.frame = CGRectMake(20, 60, 280, 50);
[remove setTitle:#"Remove First view from superview" forState:UIControlStateNormal];
[remove addTarget:self
action:#selector(removeFromView:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:remove];
[self.view release];
}
-(IBAction)removeFromView:(id)sender
{
[self.view removeFromSuperview];
}
So far, so good.
Here is where my questions begin.
1) Is this the correct approach to show/remove views. One controller (rootViewController in my case) to rule them all ?
2) Could you suggest me a way to pass data between first, second and root views ? Lets say a firstViewController is loaded. That controller contains textfield and a button. What I want is when a user type something in a textfield and a button is pressed the data in that textfield to be able to read by rootViewController and secondViewController
3) The applications works well. No crashes etc. The problem is that when neither of first or second controller are called once they can't be loaded again.
When I press a button nothing is happens.
Last but not lest, sorry about my language.
1) Is this the correct approach to show/remove views. One controller
(rootViewController in my case) to rule them all ?
It's OK. However, if the firstViewController and the secondViewController share exactly the same logic, then they are actually one class, and it is generally safe for you to remove the redundancy.
2) Could you suggest me a way to pass data between first, second and
root views ? Lets say a firstViewController is loaded. That controller
contains textfield and a button. What I want is when a user type
something in a textfield and a button is pressed the data in that
textfield to be able to read by rootViewController and
secondViewController
You can use a singleton instance to hold the shared state(, the model in the MVC pattern), but it is not recommended.
IMHO, you can use an Observer model with Key-value-observing pattern. See Apple's guide here. Or you can post NSNotification(s) from one controller to notify any other controllers that is interested in that type of notifications. See [guides here].2
3) The applications works well. No crashes etc. The problem is that
when neither of first or second controller are called once they can't
be loaded again. When I press a button nothing is happens.
In your code above, the subviews were only added when the controller ivars were nil. Try to fix that logic.

At run-time, in objective-c, how do you place a UIObject into a UIView?

At run-time, in objective-c, how to place a UIObject into a UIView?
Thank you.
You can place a UIObject in a UIView whenever you like so long as the view and object both exist (and you can create them if they don't). If the object is a subclass of UIView (which I assume it is), then just do:
[myView addSubview:myObject];
Place this code wherever you like, so long as both myView and myObject have already been created (with alloc and init, for example).
You can do something like the following --
UIImage *closeButtonImage = [UIImage imageNamed:#"closeButton.png"];
[closeButton setImage: closeButtonImage forState:UIControlStateNormal];
[self addSubview:closeButton];
When the main UIView loads it invokes viewDidLoad & viewDidAppear. You need to over-ride these methods & put your object creation in these methods.
Lets say you want to create a UILabel at runtime. You place the following code in viewDidLoad & your object gets created & added as a subview to the main view.
UILabel *newLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width-150,0,50,15)];
[newLabel setText:#"Method: "];
newLabel.textAlignment = UITextAlignmentLeft;
newLabel.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
newLabel.font = [UIFont fontWithName:#"Arial Rounded MT Bold" size:11];
[self.view addSubview:newLabel];
[newLabel release];
You need not add (or create) UI objects only in these methods. If you have defined any gestures & have specified selector methods for them, you could create & add objects in those methods too...
Allocate the UI like this for example
UIButton* btn = [[UIButton alloc] init];
[btn setFrame:desiredFrame];
[view addSubview:btn];
let your object be a Button, then use following code in your method(where you want to add object to UIView):
UIButton *objectButton = [[UIButton alloc] init];
[objectButton setImage:actualImage forState:UIControlStateNormal];
[objectButton setTag:intValue];
[objectButton addTarget:self action:#selector(anyMethod:)forControlEvents:UIControlEventTouchDown];
[self addSubview:iconImageSlide];
here [self addSubview:iconImageSlide]; add object to your view.

iPhone custom navigation bar with button doesnt click

Ive got an iPhone/iPad universal application and I wanted to have a custom navigation bar where the top half of the nav bar contained our companies logos, and the bottom half was the standard navigation bar.
I figured out how to do this, showing in the code below, but my UIButton "logosButton" doesnt always respond to being clicked, it appears as though only certain parts of the button are active, and others dont do anything at all... I cannot figure out why this is.
- (void)viewDidLoad {
[super viewDidLoad];
[[self view] addSubview: navController.view];
float width = IS_IPAD ? 768.0f : 320.0f;
float logosHeight = IS_IPAD ? 20.0f : 20.0f;
float barHeight = IS_IPAD ? 32.0f : 32.0f;
self.navBar = [[UINavigationBar alloc] initWithFrame: CGRectMake(0.0f, logosHeight, width, barHeight)];
UIView *tempView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, logosHeight)];
UIButton *logosButton = [[UIButton alloc] init];
[logosButton setBackgroundImage:[UIImage imageNamed:#"logo_bar_alone.png"] forState:UIControlStateNormal];
[logosButton setBackgroundImage:[UIImage imageNamed:#"logo_bar_alone.png"] forState:UIControlStateHighlighted];
[logosButton addTarget:self action:#selector(logosButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[logosButton setFrame: CGRectMake(0, 0, width, logosHeight)];
[tempView addSubview: logosButton];
[[self view] addSubview: tempView];
[tempView release];
[[self view] addSubview: self.navBar];
self.navItem = [[UINavigationItem alloc] initWithTitle: #"Home"];
[self.navBar pushNavigationItem:self.navItem animated:NO];
}
The method: logosButtonClicked does get fired every now and then when I click on the UIButton, but I clearly am clicking on certain spots where nothing happens at all...
Very frustrating, I dont seem, to see a pattern in regards to where its active, but could someone please help out here?
EDIT
I think I have just stumbled across something, I changed the button to a UIButtonTypeRoundedRect and got rid of the images (and also made it larger) and it appears that I cannot click on the bottom OR top sections of the button where the button is rounded. So the middle rectangular section is the only section where I can click... why would this be?
EDIT 2
For anyone reviewing this question, please see taber's latest edit on his answer. The Navigation Controller is eating touches for some reason, and I need to figure out why this is so, could be a bug?
FINAL ANSWER :)
Okay after reviewing the project it looks like the UINavigationController that is positioned below the button is eating touches globally. Which is definitely weird because it was added as a subview BELOW your button. So there must be some kind of funky frame/touch-eating going on with the UINavigationController. I tried to set navigationController.titleView.userInteractionEnabled = NO but to no avail. If anyone knows how to basically make UINavigationController and UINavigationBar not eat touches (I'm talking about the background where no buttons exist) then please chime in. The UIButton touches work just fine when the UINavigationController isn't added to the view.
Original Answer
It may be some other UI elements in [self view] or tempView sitting on top of the button and intercepting taps. You might want to try either commenting out everything else in the view(s) and/or try setting the userInteractionEnabled property on them like this:
[otherUIElement setUserInteractionEnabled: NO];
EDIT:
This code makes it so that you CAN click the round rect edges but NOT the title text. So I suspect that it's some sort of subview preventing the button from catching the tap.
[logosButton addTarget:self action:#selector(logosButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[logosButton setFrame: CGRectMake(0, 0, 320.0, 50.0)];
[logosButton setTitle: #"HELLO" forState: UIControlStateNormal];
[logosButton.titleLabel setUserInteractionEnabled: YES];
EDIT #2:
Try using this subclassed button and see if the issue still occurs:
// NavBtn.h
#interface NavBtn : UIButton {
}
-(id)initWithTitle:(NSString *)text;
#end
// NavBtn.m
#import "NavBtn.h"
#implementation NavBtn
- (id)initWithTitle:(NSString *)text {
if ((self = [super initWithFrame: CGRectMake(0, 0, 320, 50)])) {
[self setTitle: text forState: UIControlStateNormal];
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
[self setBackgroundImage:[UIImage imageNamed:#"logo_bar_alone.png"] forState: UIControlStateNormal];
[self setBackgroundImage:[UIImage imageNamed:#"logo_bar_alone.png"] forState: UIControlStateHighlighted];
}
return self;
}
// i guess you shouldn't need to mess with layoutSubviews
#end
To load it into your view:
#import "NavBtn.h"
... in viewDidLoad, etc ...
NavBtn *btn = [[NavBtn alloc] initWithTitle: #"hey"];
[self.view addSubview: btn];
[btn release];
If THAT doesn't work, there's got to be some kind of third party library doing some funky category overriding of your UIButton class or something.