I am creating an iOS app with a seating plan layout.
Trying to use an object-oriented approach, I created a class for TableLayoutObjects as they have different properties.
And to lay these TableLayoutObjects out on the screen I am representing them as UIButtons that are created as I loop through the array of TableLayoutObjects.
- (void) loadTables
{
for (TableLayoutObjects *layoutObjs in arrTableLayoutObjects)
{
if ([layoutObjs.shape isEqualToString:#"r"]) {
// rectangle
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
......
if(layoutObjs.isInteractable) {
[button addTarget:self action:#selector(tableTouchedDown:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(tableTouchedUpInside:) forControlEvents:UIControlEventTouchUpInside];
}
} else {
// text only. use label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(layoutObjs.posX, layoutObjs.posY, layoutObjs.width, layoutObjs.height)];
......
}
}
}
My event handlers look like the below for now.
// reverts back to original color and perform other instructions
- (void) tableTouchedUpInside:(UIButton *) button
{
button.layer.backgroundColor = [UIColor colorWithWhite:0.2f alpha:.5f].CGColor;
}
My question is: how do I identify the UIButtons to their TableLayoutObjects? In the event handler after I change the colour of the button, I will also want to get or set some properties of the selected TableLayoutObjects. How can I do that?
I think your example is a perfect fit for implementing a UICollectionView. Solution with the buttons is less clean and more complex.
You can set the tag of the button to the index into the arrTableLayoutObjects array of the associated item.
Alternatively, create a custom class which takes the table as a parameter and is the target of the button. This object now has direct access to the button and the table item.
Related
I am creating a few buttons programmatically, similar to:
for( int i = 0; i < 5; i++ ) {
UIButton* aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton setTag:i];
[aButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[aView addSubview:aButton];
}
And that I can implement the function of the button this way so that I know witch one was tapped:
- (void)buttonClicked:(UIButton*)button
{
NSLog(#"Button %ld clicked.", (long int)[button tag]);
}
But my question is a little bit trickier than that, I don't want just to access the button inside it's button method but also outside of it so that I can change the UIButton frame using an animation.
I was thinking that I should somehow be able to recreate the pointer to any of the buttons previously created by using the tags that I assign initially.
Can anyone point me in the right direction?
The correct way to do this is by using the next line of code:
UIButton *aButtonReconstruct = (UIButton *)[self.view viewWithTag:aTag];
where aTag is an int and is greater than 0, because all the views have the tag 0 by default, so in the for loop used in the first place, the count should start at minimum 1. In our case aTag having values from 1 to 6 after we change the for loop.
Also there shouldn't be more view with the same tag.
Hi i am making customUIButtons in my app by using the following piece of code.
+ (NSArray *) createButtonItemNormalImage:(UIImage *)normalImage
highlightImage:(UIImage *)highlightImage
disabledImage:(UIImage *)disabledImage
touchUpSelector:(SEL)selector
target:(id)target
{
// HighlightImage is not used. Highlight is shown using iOS glow
UIButton *uiButton = [UIButton buttonWithType:UIButtonTypeCustom];
uiButton.bounds = CGRectMake(0,
0,
normalImage.size.width,
normalImage.size.height);
[uiButton setImage:normalImage
forState:UIControlStateNormal];
if (disabledImage)
{
[uiButton setImage:disabledImage
forState:UIControlStateDisabled];
}
[uiButton addTarget:target
action:selector
forControlEvents:UIControlEventTouchUpInside];
uiButton.showsTouchWhenHighlighted = YES;
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:uiButton];
return [NSArray arrayWithObjects:buttonItem, uiButton, nil];
}
I have made a cancel button using the above function. The cancel button takes the user from one screen to another screen. The problem is when i come back to the first screen the cancel button is still glowing. I have seen this problem before also but a call to [self.view setNeedsLayout] used to solve it.
Why does it happen and what would be a correct way of solving it?
Thanks!
To solve this problem in not so standard way I now set the highlighted state to no for all the buttons when I enter the first screen. I use myButton.highlighted = NO;. However the documentation says following for highlighted property.
Specify YES if the control is highlighted; otherwise NO. By default, a control is not highlighted. UIControl automatically sets and clears this state automatically when a touch enters and exits during tracking and when there is a touch up.
Its not happening so in my case.I would love to know the reason behind it and standard ways of solving it
I have a multiple array of buttons called button. Each one is tagged. How do I change the image on the button based on its tag and tag only. As of right now, it only changes the very last button.
-(void)buttonTapped:(id)sender{
NSLog (#"%i",[sender tag])];
[button setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
Either:
for (UIButton *btn in button) {
if(btn.tag == 1)
{
// do something
break; // don't need to run the rest of the loop
}
}
if you want to use the array (it shouldn't be called 'button', use something with a plural for an array)
or an easier way:
UIButton *btn = (UIButton *)[self.view viewWithTag:1];
However a much simpler way would be to use the param in the callback (unless thats not the button you want). Like so:
-(void)buttonTapped:(id)sender
{
UIButton *tappedBtn = (UIButton *)sender;
[tappedBtn setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
If you just want to change the button that was tapped the following should work.
-(void)buttonTapped:(id)sender
{
NSLog (#"%i",[sender tag])];
UIButton *tappedButton = (UIButton *)sender;
[tappedButton setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
If you want to change other buttons then you can retrieve the buttons using
[self.view viewWithTag:1000]; //1000 is the tag you assigned
You don't really need to use tags in the this case. When an IBAction is invoked, the sender parameter is a pointer to the control that triggered the IBAction. (Your button.)
Thus, you already have a pointer to the button.
So, as others have pointed out, your code could read like this:
-(void)buttonTapped:(UIButton *)sender
{
[sender setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
Note that I changed the type of sender to UIButton so that you don't have to cast it. As long as the action is only ever connected to a button, this is safe to do and makes the code cleaner.
As the other poster pointed out, having an array of buttons called "button" is bad. I renamed it to "buttons" in the code below:
If you wanted to do it using tags and an array of buttons, you could use code like this:
-(void)buttonTapped:(UIButton *)sender
{
NSUInteger tag = [sender tag];
UIButton *aButton = buttons[tag];
[aButton setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
Create images with names button_change1.png, button_change2.png, etc.
And:
-(void) buttonTapped:(UIButton*)sender
{
NSString* imageName = [NSString stringWithFormat: #"button_change%ld.png", (long)sender.tag];
[button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
}
I am creating a modal view in an App, which contains several subviews. Each one of those subviews has a button and an action related to that button. I use a loop to insert each subview in place. The problem is that the first 4 subviews are ok, from the 5th to the last there are no responses.
Here it is the simplified code related to the problem:
Simplified SubviewView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// More info button
CGRect buttonFrame = CGRectMake(infoMarginLeft + infoWidthWithInfo, infoHeightControl, 25, 25);
UIButton * button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[button setFrame:buttonFrame];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}
-(void)buttonTapped:(id)sender
{
NSLog(#"Button tapped!";
}
Simplified view.m
- (void)drawRect:(CGRect)rect
{
for (int i = 0; i < 12; i++) {
// Color info container
SubviewView * miniView = [[SubviewView alloc] initWithFrame:CGRectMake(0, 20 * i, 15, 15)];
miniColorView.backgroundColor = [UIColor clearColor];
// Adding subview through array to keep track of objects
[self addSubview:miniColorView];
}
}
Thanks in advance, I have absolutely no ideia of what's going on :p
--
EDIT
Just found out what it was!
The parent view had as height the screen height. The thing is, some of the subviews were beyond the screen height limit. All I had to do was increase the parent view height =)
I would check to make sure you are not adding your view miniView over top of your button.
Also this should be moved outside of drawRect, just put this in your init method.
Edit:
Set your miniView's background color to another besides clear, and see if you still see your button. If not, then you covered up your button, and it won't receive touch events.
In my app, I need to parse some data from the network, and add some customized buttons.
Later, when user click on it, I would provide more details.
An image view is the background for the app
The position of these buttons(xPos, yPos) are parsed from the server(dynamic data)
no prints when I click on these buttons that I add programmatically
The code I have for adding it is like this
...
[businessButton setImage:businessImage forState:UIControlStateNormal];
[businessButton setFrame:CGRectMake([xPos floatValue], [yPos floatValue], [businessImage size].width/2, [businessImage size].width/2)];
[businessButton addTarget:self.imageView action:#selector(serviceProviderSelected:) forControlEvents:UIControlEventTouchUpInside];
...
- (void)serviceProviderSelected:(id)sender
{
NSLog(#"sp tapped\n");
}
I created another dummy app to do (what I think is the same thing), and the button works out fine...
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *customizedButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [UIImage imageNamed:#"business-icon.png"];
[customizedButton setImage:image forState:UIControlStateNormal];
[customizedButton setFrame:CGRectMake(100, 100, 20, 20)];
[customizedButton addTarget:self action:#selector(customButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:customizedButton];
}
- (IBAction)buttonPressed:(id)sender {
NSLog(#"yes, pressed\n");
}
I've been suspecting that the buttons I create in code are already released, and printed out the array that I use for storing these Buttons, they are valid addresses though...
Thanks!
William
I just found out why.
Instead of [self.imageView addSubview:businessButton];, [self.view addSubview:businessButton]; works now.
I think I didn't really understand how the view relationship was after adding an imageView in storyboard.
UIImageView has user interactions disabled by default. you have to set this property to YES in order to get an UIButton or anything else working as it's subview
You need to do:
[businessButton addTarget:self action:#selector(serviceProviderSelected:) forControlEvents:UIControlEventTouchUpInside];
The target needs to be the class that defines the selector.