I'm having trouble adding button to my UITableViewCell, the cell has two UILabels and two UIImageViews, sometimes the UIImageView will contain an image and sometimes a button:
In my UITableViewCell subclass I have:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if ( self ) {
// Initialization code
firstLock = [[UILabel alloc]init];
[firstLock setBackgroundColor:[UIColor clearColor]];
firstLock.textAlignment = UITextAlignmentLeft;
firstLock.font = [UIFont fontWithName:#"Arial-BoldMT" size:17];
secondLock= [[UILabel alloc]init];
[secondLock setBackgroundColor:[UIColor clearColor]];
secondLock.textAlignment = UITextAlignmentRight;
secondLock.font = [UIFont fontWithName:#"Arial-BoldMT" size:17];
firstLockImage = [[UIImageView alloc]init];
secondLockImage = [[UIImageView alloc] init];
[self.contentView addSubview:firstLock];
[self.contentView addSubview:secondLock];
[self.contentView addSubview:firstLockImage];
[self.contentView addSubview:secondLockImage];
}
return self;
}
When one of the UIImageViews is just an image no problem, but it crashes when I add a UIButton (imaged) as a subview.
In the UITableViewDataSource implementation:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImage *btnImage = [UIImage imageNamed:#"bike_ok.png"];
UIButton *button =[UIButton alloc];
[button setImage:btnImage forState:UIControlStateNormal];
[cell.secondLockImage addSubview:button];
Adding a button as a subview of the image view crashes:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Requesting the window of a view (<UIButton: 0x7bac7d0; frame = (0 0; 0 0); transform = [0, 0, 0, 0, 0, 0]; alpha = 0; opaque = NO; userInteractionEnabled = NO; layer = (null)>) with a nil layer. This view probably hasn't received initWithFrame: or initWithCoder:.'
*** First throw call stack:
What am I missing?
Thanks!
Just add! its important this line
[firstLockImage setUserInteractionEnabled:YES];
[secondLockImage setUserInteractionEnabled:YES];
Because the UIImageView has NO as default and the button doesn't work without it!
If you read the crash error it is quite easy to see where your issue is. You have not initialized your button.
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0,0,0,0)];
or you can use initWithCoder.
Hope this helps
Sam
Read the exception text - it says:
This view probably hasn't received initWithFrame: or initWithCoder:
Just a few lines up in your question, you are sending messages to a UIButton instance that you have only alloc'd and not sent any init... message to. That is your error.
Furthermore, you shouldn't directly call the alloc/init pair on UIButton as it's a class cluster and you should usually use +[UIButton buttonWithType:] to get a button instance.
EDIT I'm not 100% sure about that, actually. But you don't really know exactly what you'll get if you do initWithFrame: so I'd still go with buttonWithType: to get a custom button, which is what you want. END EDIT
So, change that line to:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
Hope this helps!
change UIButton *button =[UIButton alloc]; (alloc w/o init?) to
UIButton *button =[UIButton buttonWithType:UIButtonTypeCustom];
//set frame
[button setFrame:(CGRect){x,y,w,h}];
+buttonWithType handles the alloc/init for your UIButton object
Related
I making some simple AR app with interfaces using overlaySKScene.
In viewDidLoad, I applied overlay scene in main ARSCNView(OverlayScene is my class inherited SKScene).
self.sceneView.overlaySKScene = [[OverlayScene alloc] initWithSize:self.sceneView.bounds.size];
self.sceneView.overlaySKScene.delegate = self;
Then, I added UIkit button in OverlayScene init.
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 100)];
[button setTitle:#"button" forState:UIControlStateNormal];
button.layer.position = CGPointMake(self.view.frame.size.width/2, 200);
[self.view addSubview:button];
But, that button didn't appear on screen.
When I added sprite node in this scene, it worked.
Is there other method for it or bug?
Thank you.
OverlayScene's init isn't really an appropriate place to do that, as self.view will equal nil because when a scene is initialize it has yet to be added to the view hierarchy. A better choice might be the SKScene lifecycle method - (void)didMoveToView:(SKView *)view; like:
- (void)didMoveToView:(SKView *)view {
[super didMoveToView:view];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 100)];
[button setTitle:#"button" forState:UIControlStateNormal];
button.layer.position = CGPointMake(self.view.frame.size.width/2, 200);
[self.view addSubview:button];
}
- (void)viewDidLoad{
int leftBorder = 80;
int topBorder = 160;
int width = 150;
int height = 50;
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(leftBorder, topBorder, width, height)];
myView.layer.cornerRadius = 5;
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
UIButton *testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testButton.frame = CGRectMake(0, 0, 50, 50);
[testButton setTitle:#"testButton" forState:UIControlStateNormal];
[testButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.myView addSubview:self.testButton];
self.myView.hidden = YES;
[super viewDidLoad];
}
Hi, sorry for stupid question! I'm newby in xcode. Why I didn't see this button? And How can I hide button after click? I need button inside the frame.
Simple remove self.myView.hidden = YES;
To add you click listener, two solution:
By code in your viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[mybutton addTarget:self action:#selector(myButtonClick:) forControlEvents:(UIControlEvents)UIControlEventTouchDown];
}
- (void)myButtonClick:(id)sender {
myButton.hidden = YES;
}
Or via interface Builder (preferred), The easiest way is to actually define the handler/action in Xcode using the IBAction declaration in the interface file (add the declaration just before the #end statement). Then attach the action to the button
you are adding self.testButton instead of the created testButton.
[self.myView addSubview:testButton];
you did not assign myView to your property.
[self.view addSubview:myView]; self.myView = myView;
remove self.myView.hidden = YES;
Another remark:
You should usually call super as early as possible. Otherwise, the superclass may interfere with your own implementation.
There are several issues in your code.
You add testButton as subview to self.myView. Then you hide self.myView. As a result neither self.myview nor its subviews will be visible.
You get confused with your local varialbles and instance varialbes. Apparently there is an instance variable and property myView. Otherwise you coudn't use self.myView. And you declare a local variable myView, which is not the same as the instance variable. This may well be perfectly ok. But I got a guts feeling that you did this not intentionally. Your self.myView may well be nil at the point in time where you add the subview. And even the subview self.testButton may be nil. That will compile file and execute fine but nothing does happen actually.
The same for testButton.
I suggest to change the code a bit, assuming there are properties for myView and testButton of the appropriate types:
self.myView = [[UIView alloc] initWithFrame:CGRectMake(leftBorder, topBorder, width, height)];
self.myView.layer.cornerRadius = 5;
self.myView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.myView];
self.testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.testButton.frame = CGRectMake(0, 0, 50, 50);
[self.testButton setTitle:#"testButton" forState:UIControlStateNormal];
[self.testButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.myView addSubview:self.testButton];
self.myView.hidden = NO;
I am customizing a UITableViewCell with several subviews:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_mainView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 40.0f)];
_hiddenOptionsView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 40.0f)];
_menuView = [[CellMenuView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 20.0f)];
[self addSubview:_menuView];
[self addSubview:_hiddenOptionsView];
[self addSubview:_mainView];
}
return self;
}
The class CellMenuView is a UIView subclass which has two UIButtons with their counterpart target actions setup on initialization:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIImageView *background = [[UIImageView alloc] initWithFrame:frame];
[background setImage:[UIImage imageNamed:#"cell_menu_bg.png"]];
[self addSubview:background];
CGFloat buttonX = frame.origin.x;
CGFloat buttonY = frame.origin.y + 3.0f;
CGFloat buttonWidth = frame.size.width / 2.0f;
CGFloat buttonHeight = 10.0f;
_editButton = [UIButton buttonWithType:UIButtonTypeCustom];
_editButton.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
_editButton.backgroundColor = [UIColor clearColor];
_editButton.titleLabel.shadowColor = [UIColor blackColor];
_editButton.titleLabel.shadowOffset = CGSizeMake(0.0f, -1.5f);
_editButton.titleLabel.font = [UIFont boldSystemFontOfSize:22.0f];
[_editButton setTitle:#"Edit" forState:UIControlStateNormal];
[_editButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[_editButton addTarget:self action:#selector(editButton:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_editButton];
_fireButton = [UIButton buttonWithType:UIButtonTypeCustom];
_fireButton.frame = CGRectMake(buttonWidth, buttonY, buttonWidth, buttonHeight);
_fireButton.backgroundColor = [UIColor clearColor];
_fireButton.titleLabel.shadowColor = [UIColor blackColor];
_fireButton.titleLabel.shadowOffset = CGSizeMake(0.0f, -1.5f);
_fireButton.titleLabel.font = [UIFont boldSystemFontOfSize:22.0f];
[_fireButton setTitle:#"Fire" forState:UIControlStateNormal];
[_fireButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[_fireButton addTarget:self action:#selector(fireButton:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_fireButton];
return self;
}
}
I have implemented the target action methods but they aren't being executed. Instead, didSelectRowAtIndexPath: takes precedence over the touch up events inside buttons every time I pressed one of them.
The only way I have been able to fire the events on buttons is making sure that one of the letters in UIButton's title is touched (using the simulator, of course).
I must say that _menuView is hidden behind the other subviews and shown below the cell when a custom accessory button in cell is pressed. It appears by modifying the Y origin and disappears by setting it to 0.0f again.
I think it may be related to the view hierarchy, because I haven't had any problems in the past by adding buttons directly to the cell. But I'm just guessing here.
How can I make the events on buttons take precedence over the didSelectRowAtIndexPath: method?
Thanks!
Implement
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
And return nil for the indexPath you don't want selected; this will prevent didSelectRowAtIndexPath from being called on that cell, and (hopefully), your custom action will be called.
Update: After implementing this myself (adding borders to the buttons), this is what I have.
The code to achieve this (which is different than yours), is as follows:
CellMenuView.m
self.backgroundColor = [UIColor blackColor];
_editButton.layer.borderColor = [UIColor whiteColor].CGColor;
_editButton.layer.borderWidth = 2;
_fireButton.layer.borderColor = [UIColor whiteColor].CGColor;
_fireButton.layer.borderWidth = 2;
UITableViewCell subclass
// Change in height to make it taller
_menuView = [[TestBtn alloc] initWithFrame:CGRectMake(0.0f, 20.0f, 320.0f, 60.0f)];
UITableViewController subclass
self.tableView.rowHeight = 100;
After testing, mine works as expected. If I click within the white squares, the button action is performed as normal, and didSelectRowAtIndexPath is not called. didSelectRowAtIndexPath is only called when I click outside of the buttons.
I think the issue here might be your button heights/frames, as well as your row height. Add borders to your buttons, to check its clickable area, and increase the height of the button to increase the clickable area.
I have created a class called UICustomButton, which is a subclass of UIView. I added a UIButton to UIView as a subview as shown in my code below:
-(id)initWithButtonType:(NSString *)type
{
self = [super init];
if (self)
{
self.customButton = [self setupButtonWithTitle:type andFrame:frame];
[self addSubview:self.customButton];
self.customButton addTarget:self action:#selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
The problem I am facing is that although the button appears, they are not clickable. Is there something wrong with the way I am adding the buttons to the UIView?
EDIT: To add on, I am using this custom button class instance to a cell:
UICustomButton *customButton = [[UICustomButton alloc]initWithFrame:someFrame];
[cell.contentView addSubView:customButton];
check the frame of your UICustomButton object, and if self.customButton is out of its superView.
Make sure that the frame of the subview is within the frame of its superview. I encountered the issue twice and both times the frame of the subview was incorrect.
You should check if all properties userInteractionEnabled are on:
Check that property for UITableView where your cells with this view are displayed
Check that property for UITableViewCell where you add this view as subview
Check that property for UICustomButton
Check that property for customButton in -(id)initWithButtonType:(NSString *)type
My button was not working because i had two views in each other. The first view, as you can see, has a width and heigth of 0 pixels. I've made this view the same size as view2 and then my button was clickable.
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(325, 346, 0, 0)];
view1.alpha = 0.90;
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 450, 150)];
[view2.layer setCornerRadius:10.0f];
view2.backgroundColor = [UIColor whiteColor];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self action:#selector(addClosedToCollection) forControlEvents:UIControlEventTouchUpInside];
[btn setFrame:CGRectMake(30, 30, 300, 32)];
[btn setTitle:#"i'm a button" forState:UIControlStateNormal];
[view2 addSubview:btn];
[view1 addSubview:view2];
i hope i've helped somebody out :)
Your outer view probably has a height of 0. Add constraints that makes it the same height as (or higher than) the subviews, or create it with a frame that is large enough.
try putting this after the if statement:
self.isTouchEnabled = YES;
I'm adding a custom view to a tableHeaderView with the following code:
imageButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
imageButton.frame = CGRectMake(120, 12, 64, 64);
imageButton.titleLabel.font = [UIFont systemFontOfSize:10];
imageButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
imageButton.titleLabel.textAlignment = UITextAlignmentCenter;
[imageButton setTitle:NSLocalizedString(#"Choose\nPhoto", #"Choose\nPhoto") forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(photoButtonPressed) forControlEvents:UIControlEventTouchUpInside];
// add existing image, if any, to button
if (child.thumbnailImage != nil) {
[imageButton setBackgroundImage:child.thumbnailImage forState:UIControlStateNormal];
}
// add button to view
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(22, 12, 70, 70)];
[headerView addSubview:imageButton];
// add view to table header
self.tableView.tableHeaderView = headerView;
The memory leak is showing on the alloc line above for the headerView UIView.
I'm declaring the UIView and the UIButton in the header file and releasing it in ViewDidUnload and dealloc, so I'm not sure what I'm doing wrong.
Also, this is only showing up on the device and not the simulator (just thought I'd mention).
Any help would be appreciated.
Thanks,
Rod
You're using the headerView setter method (self.headerView) so you need to release the UIView instance you assign to the headerView property, either using release or autorelease.
e.g.
UIView* newHeaderView = [[UIView alloc] initWithFrame...];
self.headerView = newHeaderView;
[newHeaderView release];
or
self.headerView = [[[UIView alloc] initWithFrame:...] autorelease];
The reason is because the headerView setter method automatically retains the object assigned to it.
Alternatively, you can set the headerView instance variable directly, without using the property setter method:
[headerView release]; // release any existing object
headerView = [[UIView alloc] initWithFrame:...];