UIButton never responds within a UIImageView's frame area - iphone

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.

Related

UIAlertView Buttons(subviews) is not Showing in iOS 7

UIAlertView is working fine in ios 6 with below code .But when it comes to ios 7 the subviews ( "yes" and "no" buttons in my code ) is not showing when alertview is called only text message is showing .Can anyone tell me how to resolve this problem ?
viewController.m file
[Utilities prCustomAlert:#"Textmessage" inTitle:#"Alert view title" delegate:self inTag:300];
CustomAlertView *alertView = [Utilities sharedUtility].customAlertView;
alertView.numberOfBtns = 2;
UIButton *btn= (UIButton *)[alertView viewWithTag:10];
[btn setTitle:#"no" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(dontlogout) forControlEvents:UIControlEventTouchDown];
btn = (UIButton *)[alertView viewWithTag:11];
[btn setTitle:#"yes" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(logout) forControlEvents:UIControlEventTouchDown];
[Utilities displayCustomAlertForDelegate:self];
UIAlertView.m file
CGRect viewFrame = self.frame;
CGRect buttonFrame = button.frame;
if(self.numberOfBtns==2){
CGRect labelFrame = [self viewWithTag:15].frame;
button.frame = CGRectMake(10, 0, 40, 30);
button.hidden = NO;
//yes...
btn = (UIButton *)[self viewWithTag:11];
btn.frame = CGRectMake(60, 0, 40, 30);
btn.hidden = NO;
//no..
btn = (UIButton *)[self viewWithTag:10];
btn.hidden = YES;
}
We can add subviews to UIAlerView by adding subview to the presentedViewController's view when UIAlertView is presented. I have accessed UIAlertView like following way :
NSArray *subviews = [UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController.view.subviews;
I have created a subclass of UIAlerView :
Header File :
#interface MLKLoadingAlertView : UIAlertView
- (id)initWithTitle:(NSString *)title;
#end
Implementation File :
#import "MLKLoadingAlertView.h"
#define ACTIVITY_INDICATOR_CENTER CGPointMake(130, 90)
#implementation MLKLoadingAlertView
- (id)initWithTitle:(NSString *)title
{
if ( self = [super init] )
{
self.title = title;
self.message = #"\n\n";
[self setDelegate:self];
}
return self;
}
// You can Customise this based on your requirement by adding subviews.
- (void)didPresentAlertView:(UIAlertView *)alertView
{
NSArray *subviews = [UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController.view.subviews;
if( subviews.count > 1 )
{
// iOS while presenting an alertview uses a presening view controller. That controller's view has several subviews. I have picked one
// subview from it which has frame similar to the alertview frame.
UIView *presentedView = [subviews objectAtIndex:1];
UIActivityIndicatorView *customActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[customActivityIndicator startAnimating];
customActivityIndicator.center = ACTIVITY_INDICATOR_CENTER;
[presentedView addSubview:customActivityIndicator];
}
}
#end
In - (void)didPresentAlertView:(UIAlertView *)alertView method I have added the subviews to UIAlertView by accessing Presented View Controller's view.
You can find explanation and code example for this on Here
What you were doing was always wrong. You are not allowed to add your own subviews to a UIAlertView. The good news is - in iOS 7, you don't have to! The new custom transition animation mechanism lets you make your own view that behaves just an alert view, but since it is your view, you can put anything you like into it, like this:
Note how the fake "alert view" floats in front of the original view in the screen shot on the right, and dims the screen behind it, just like a real alert view. But it is made up entirely of custom content; a "real" alert view could never contain an image and a switch!
For the code that creates this view, which you can easily adapt to your own purposes, see my github site: https://github.com/mattneub/custom-alert-view-iOS7
I think the root of the problem in iOS7 that Apple changed the UIAlertView appearance mechanism.
From now any show of alertView follows after initiating of two private view controllers
_UIModalItemAppViewController
_UIModalItemsPresentingViewController
In other words, now UIAlertView is not a pure view - it is a part of some complicated collection of view controllers with full view controller life cycle.
But a good news that you can change accessoryView to your customContentView in a standard alert view
[alertView setValue:customContentView forKey:#"accessoryView"];
Note that you must call this before [alertView show].

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.

remove UIImageView from its Superview

In my application when the user touches the screen, I put an UIImageView on the screen like this:
- (void) drawPoint:(CGPoint) toLocation {
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(toLocation.x, toLocation.y, SIZE_X, SIZE_Y)];
image.tag = 1;
[image setImage:[UIImage imageNamed:#"point.png"]];
[self.myView addSubview:image];
[image release];
}
Here MyView is an UIView.
When the user has finished touching, I want to remove my UIImageView. I have tried this:
- (void) removeFromPoint:(CGPoint) location{
UIImageView *image;
[[image viewWithTag:1] removeFromSuperview];
}
or
- (void) removeFromPoint:(CGPoint) location{
UIImageView *image = (UIImageView *)[self.MyView viewWithTag:1];
[image removeFromSuperview];
}
but both of them end up in EXC_BAD_ACCESS.
Do you know how can I accomplish this?
Thanks.
If you want to remove uimage under the touch try this:
UIView * imageView = [self.myView hitTest:location withEvent:nil];
if ([imageView isKindOfClass:[UIImageView class]] && imageView.tag == 1) {
[imageView removeFromSuperview];
}
(note I've typed it here, i haven't test that code)
If you wish to debug your code enable NSZombies and check why you gets the bad_access. (check this to learn more about NSZombieEnabled http://www.cocoadev.com/index.pl?NSZombieEnabled)
If you are going to add only one view. Then I would store it in a retained property and I would add/remove it on events like this:
#property (nonatomic,retain) UIImageView* marker;
#interface
#syntesize marker;
-(void) onTouchDown {
[self.view addSubview:marker];
}
-(void) onTouchUp {
[marker removeFromSuperview];
}
The first example of removing image view is wrong.
In the second one what is POINT_TAG equal to?
Try to use tag greater then 9.
Try to debug what UIImageView *image = (UIImageView *)[self.MyView viewWithTag:POINT_TAG];
returns.

UIButton and UIPicker

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

UIButton border and background image

What I Want: A border indicating if a UIButton is selected or not.
Background: I've got some UIButtons using transparent images, not text. These are toggle buttons (i.e. can be on or off).
Problem: The UIButton class gives users no indication of whether a button is selected or not unless you change something else about the button. Since the image doesn't change with the state, I'd need two of every image, one normal, one selected and set one for each state of the button. This is annoying. I thought instead I'd change the background image, but this removes the pretty border on the button, I just get a rectangle of my background image, yuck.
Possible solutions I don't like:
1) Create a background that matches the UIButton border and use that for selected. I don't like this because they wont match perfectly and I'm picky.
2) Create two images for each button, essentially identical but with a different background. This seems like unnecessary work, and since this problem is coming up repeatedly, I want a solution for the future as well.
I hope somebody's figured out a decent solution to this by now. Thanks in advance.
Since UIButton has two image layers, an image and a background image, I think you could accomplish what you want by using just two background images for all your buttons. One image shows a border and the other does not. Swap the backgrounds out when the control state changed.
//
// TabBarSingleton.h
#import <Foundation/Foundation.h>
#interface TabBarSingleton : UITabBarController <UITabBarControllerDelegate>{
NSRecursiveLock *barLock;
UIButton *Button;
UIButton *favoriteButton;
}
#property(nonatomic, retain) UIButton *Button;
#property(nonatomic, retain) UIButton *favoriteButton;
- (void) ButtonPressed;
- (void) favoriteButtonPressed;
#end
///////////////////////////////////
If you want the the borders only, then you have only one choice of using two images for the two states otherwise if your purpose is to differentiate between two states then you can do it by changing alpha a little bit of the selected button this will give the effect like toggle buttons, you can also disable the selected button and enable it again when the other button is pressed.
Hope this will give you a fair idea.
//
// TabBarSingleton.m
// Created by ArunDhwaj on 9/7/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "TabBarSingleton.h"
#implementation TabBarSingleton
#synthesize Button, favoriteButton;
- (id) init
{
if (self = [super initWithNibName: nil bundle: nil])
{
barLock = [[NSRecursiveLock alloc] init];
}
self.delegate = self;
return self;
}
+ (TabBarSingleton *) defaultBar
{
}
- (void)viewDidLoad
{
NSLog(#"TabBarSingleton: viewDidLoad");
//Hiding TabBar
self.tabBar.hidden = YES;
//Creating a UIView, its frame is same as tabBar frme
CGRect tabbarFrame = self.tabBar.frame;
UIView* customTabbarView = [[UIView alloc] initWithFrame:tabbarFrame];
UIImageView *newsFeedImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"newsfeeds_normal.png"]];
newsFeedImg.frame = CGRectOffset(newsFeedImg.frame, 0, 1);
Button = [UIButton buttonWithType:UIButtonTypeCustom];
[Button setFrame:newsFeedImg.frame];
[Button setBackgroundImage:newsFeedImg.image forState:UIControlStateNormal];
[Button setBackgroundImage:[UIImage imageNamed:#"newsfeeds_active.png"] forState:UIControlStateHighlighted];
[Button addTarget:self action:#selector(newsFeedsButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[customTabbarView addSubview:Button];
//[newsFeedImg release];
CGRect newsFeedFrame = newsFeedImg.frame;
UIImageView *favoriteImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"favorites_normal.png"]];
favoriteImg.frame = CGRectMake(newsFeedFrame.size.width, newsFeedFrame.origin.y, newsFeedFrame.size.width, newsFeedFrame.size.height);
favoriteButton = [UIButton buttonWithType:UIButtonTypeCustom];
[favoriteButton setFrame:favoriteImg.frame];
[favoriteButton setBackgroundImage:favoriteImg.image forState:UIControlStateNormal];
[favoriteButton setBackgroundImage:[UIImage imageNamed:#"favorites_active.png"] forState:UIControlStateHighlighted];
[favoriteButton addTarget:self action:#selector(favoriteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[customTabbarView addSubview: favoriteButton];
//[favoriteImg release];
[self.view addSubview:customTabbarView ];
[self newsFeedsButtonPressed];
}
- (void) newsFeedsButtonPressed
{
NSLog(#"TabBarSingleton: newsFeedsButtonPressed");
self.selectedIndex = 0;
//Keeping Highlighted newsFeed tab
UIImageView *newsFeedImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"newsfeeds_active.png"]];
[Button setImage: newsFeedImg.image forState:UIControlStateNormal];
//Keeping normal others tab icons
UIImageView *favoriteImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"favorites_normal.png"]];
[favoriteButton setImage: favoriteImg.image forState:UIControlStateNormal];
}
- (void) favoriteButtonPressed
{
NSLog(#"TabBarSingleton: favoriteButtonPressed");
self.selectedIndex = 1;
//Keeping Highlighted newsFeed tab
UIImageView *newsFeedImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"newsfeeds_normal.png"]];
[Button setImage: newsFeedImg.image forState:UIControlStateNormal];
//Keeping normal others tab icons
UIImageView *favoriteImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"favorites_active.png"]];
[favoriteButton setImage: favoriteImg.image forState:UIControlStateNormal];
#pragma mark UITabBarControllerDelegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
NSLog(#"TabBarSingleton: shouldSelectViewController");
return YES;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"TabBarSingleton: didSelectViewController");
}
- (void) dealloc
{
//[barLock release];
[super dealloc];
}
#end