Dynamic Button Layout without hard coding positions - iphone

I would like to implement a view (it will actually be going into the footer of a UITableView) that implements 1-3 buttons. I would like the layout of these buttons (and the size of the buttons themselves) to change depending on which ones are showing. An example of almost the exact behavior I would want is at the bottom of the Info screen in the Contacts application when you are looking at a specific contact ("Add to Favorites" disappears when it is clicked, and the other buttons expand to take up the available space in a nice animation).
As I'm running through the different layout scenarios, I'm hard coding all these values and I just think... "this is NOT the way I'm supposed to be doing this."
It even APPEARS that Interface Builder has some features that may do this for me in the ruler tab, but I'll be darned if I can figure out how they work (if they work) on the phone.
Does anyone have any suggestions, or a tutorial online that I could look at to point me in the right direction?
Thanks much.

You could take a look at
Stanford cs193p classes
I think there is a demo in one of the lessons/example where they programaticaly set the autoresize of the buttons and some other where they animate the view.
you can also make the view set dynamically the width of each of its subview to (ownWidth/numberofSubView),the same for the positon of each elements.
[edit] like that ? it's not "dynamic" yet, but you've got the idea
you can increase/decrease numberOfbuttons when "adding/removing" a button then reset the new frame of all the subviews
- (void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
view.backgroundColor = [UIColor lightGrayColor];
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
int numberOfbuttons = 2;
CGFloat buttonsLegth = (-20+view.bounds.size.width)/numberOfbuttons-20;
for(int i=0;i<numberOfbuttons;i++){
CGRect buttonFrame = CGRectMake(0, 0, buttonsLegth, 37);
buttonFrame.origin.x = (buttonFrame.size.width+20)*i + 20.0;
buttonFrame.origin.y = 20.0;
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // Autoreleased
button.frame = buttonFrame;
[button setTitle:#"Done" forState:UIControlStateNormal];
[view addSubview:button];
}
self.view = view;
[view release];
}

hi remove all buttons in view and add again using this method...
you have to pass number of buttons and a UIView in which you want to add these buttons
-(void)addButtons:(int)numberOfButtons ToView:(UIView *)containerView;
{
CGRect containerRect = containerView.frame;
CGFloat padding = 5.0;
CGFloat width = (containerRect.size.width - (padding*numberOfButtons+1))/numberOfButtons;
for (int i = 1; i<=numberOfButtons; i++)
{
UIButton * btn = [[[UIButton alloc] initWithFrame:CGRectMake(i*padding+width, 0, width, containerRect.size.height)]autorelease];
//set Button Properties like Target,backColor,Title,,,,etc MUST SET TAG TO ACCESS THEM IN CLICK METHOD IF YOU ARE USING SAME SELECTOR FOR ALL BUTTONS.
[containerView addSubview:btn];
}
}
Try this and have fun......

Related

How to add View below other view in ViewController in iOS?

In my app i am creating view controller with mixed of UILabel and UITextview which want to be scrollable because the text are dynamic and also exceeds vertical screen size.
I am currently having Scrollview which has subviews as below. Views are created in Xcode 4.3 Storyboard.
UILabel1(Say Heading)
UITextView1(Dynamic text which can be any size)
UILabel2(Second Heading)
UITextView2(Dynamic text which can be any size)
and so on.
The problem is
When the UITextView1 has more content then it overlaps with UILabel2 which i don't want.
I would like to have UILabel1 on top of scrollView and UITextView1 below the UILabel1. UILabel2 below UITextView1 and so on.
What i have to do to achieve this?
EDIT
In Storyboard
![enter image description here][1]
In Simulator
![enter image description here][2]
Thanks for your help guys. Much appreciated.
Code
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[scrollView setScrollEnabled:YES];
[self.view addSubview:cockTailNameLabel];
[self.view insertSubview:txtIngredients belowSubview:cockTailNameLabel];
[self.view insertSubview:scrollView belowSubview:cockTailNameLabel];
//[scrollView]
[self.cockTailNameLabel setText:self.passcockTailName];
[_txtUse setText:self.passUse];
[_txtUse setEditable:NO];
[_txtUse setUserInteractionEnabled:NO];
CGRect useFrame = _txtUse.frame;
useFrame.size.height = _txtUse.contentSize.height;
_txtUse.frame = useFrame;
[txtIngredients setText:self.passIngredients];
[txtIngredients setEditable:NO];
[txtIngredients setUserInteractionEnabled:NO];
CGRect ingredientFrame = txtIngredients.frame;
ingredientFrame.size.height = txtIngredients.contentSize.height;
txtIngredients.frame = ingredientFrame;
[txtRecipe setText:self.passReceipe];
[txtRecipe setEditable:NO];
[txtRecipe setUserInteractionEnabled:NO];
CGRect recipeFrame = txtIngredients.frame;
recipeFrame.size.height = txtRecipe.contentSize.height;
txtRecipe.frame = recipeFrame;
[scrollView insertSubview:_txtUse belowSubview:cockTailNameLabel];
[scrollView insertSubview:titleIngredients belowSubview:_txtUse];
[scrollView insertSubview:txtIngredients belowSubview:titleIngredients];
[scrollView insertSubview:btnReceipe belowSubview:txtIngredients];
[scrollView insertSubview:btnHome belowSubview:txtIngredients];
[scrollView insertSubview:txtRecipe belowSubview:btnHome];
[scrollView insertSubview:btnfacebookShare belowSubview:txtRecipe];
[scrollView insertSubview:btnTwitterShare belowSubview:txtRecipe];
/*[scrollView addSubview:_txtUse];
[scrollView addSubview:titleIngredients];
[scrollView addSubview:txtIngredients];
[scrollView addSubview:btnReceipe];
[scrollView addSubview:btnHome];
[scrollView addSubview:txtRecipe];
[scrollView addSubview:btnfacebookShare];
[scrollView addSubview:btnTwitterShare];*/
[scrollView setContentSize:CGSizeMake(320, 1000)];
NSLog(#"RecipeName :%# ",passcockTailName);
}
In Storyboard or IB you can rearrange them freely.
In code you do - (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview.
In code (in viewDidLoad):
UIScrollView *scroll =[[UIScrollView alloc] initWithFrame:CGrectMake(0,0 320, 480)];
[self.view addSubview:scroll];
// code to init your UI object
UILabel *uilabel1 = [[UIlabel alloc] initWithFrame:CGrectMake(10,10, 100, 40)]; // example
uilabel1.font = [UIFont systemFontOfSize:10];
uilabel1.text = #"UILabel1";
uilabel1.backgroundColor = [UIColor clearColor];
//
//
UILabel *uilabel2 = [[UIlabel alloc] initWithFrame:CGrectMake(10, 10 + 10 + uilabel1.frame.origin.y, 100, 40)]; // example
uilabel2.font = [UIFont systemFontOfSize:10];
uilabel2.text = #"UILabel2";
uilabel2.backgroundColor = [UIColor clearColor];
//
//
[scroll addSubview:uilabel1];
[uilabel1 releale];
[scroll addSubview:uilabel2];
[uilabel2 releale];
//
//
// in end
float offset = 10.0 * 2; // offset between uiobjects, N - number of objects
scroll.contentSize = CGSizeMake (0, 0, uilabel1.frame.size.height + uilabel2.frame.size.height + offset, 320);
Note that you may set frame (and other properties) of yours uiobjects and adds it in order to descend.
You can tell next view to start from the end of the first view like so:
startYOfNextView = firstView.frame.origin.y position + firstView.frame.size.height;
Do the same for the rest of the other view. If your view has variable text length, you may need to precaculate the height of the string based on a specific font e.g.:
CGSize maxSize = CGSizeMake(widthOfView, 9999999);
CGSize expectedSize = CGSizeMake(stringVar sizeWithFont:[UIFont fontWithName:#"Arial"] withMaxSize:maxSize lineBreakMode:UILineBreakModeWordWrap);
then tell your dynamic view to use the height of expectedSize variable like so:
myDynamicView = [[UILabel alloc] initWithFrame:CGRectMake(..., expectedSize.height)];
Your issue is labels are coming on top of the textview (Overlapped), right?
In This case, you are modifying the height of the textview dynamically. If its just for display purpose you can set the text to a UILabel with numberOfLines = 0 instead of UITextview; As its added to scrollview adding a label will be fine.
Also you need to modify the origin.y of the remaining views so that it will be properly aligned. ie, secondView.origin.y = firstView.origin.y + firstView.size.height + offset. Likewise modify the origin of the remaining views wrt the just above view. So that its origin will lie outside the previous view frame.
In my case I had to project it over the parent view, so remember there is also something,
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview
I think adding a transparent UI button is the fastest solution. You could add it once and then show / hide whenever you need to disable all views below the current one (assuming the "current" view, say a popup, was added last to your superview).
Declare an instance variable UIButton* _parentDisablingOverlay; in your popup view, then add this in its init or layoutSubviews method:
_parentDisablingOverlay = [[UIButton alloc] initWithFrame:CGRectMake(0,0, self.superview.frame.size.width, self.superview.frame.size.height)];
[self.superview insertSubview:_parentDisablingOverlay belowSubview:self];
_parentDisablingOverlay.hidden = true;
Later on, when you want to show the popup and disable everything below:
- (void) open {
self.hidden = false;
_parentDisablingOverlay.hidden = false;
}
Similarly to close and re-enable everything:
- (void) close {
self.hidden = true;
_parentDisablingOverlay.hidden = true;
}

iPhone - UIbutton nested in UIView does not respond to touch following animation of that view

I have a UIView (view B) with a UIButton on it.
I add this view B to my main view (view A) in an area outside of the bounds of the main view and I than animate it in with a UIView animation.
After the UIView animation is complete and View B is now on top of View A so the button is visible, the button does not respond to touches... I have never seen this before but this is the first app I am doing with the new iOS (iOS 5). Any ideas?
Thanks in advance.
Is this the situation you are describing? Because it seems to work fine. Did you check if userInteractionEnabled is set to YES on the UIView?
- (void)buttonPressed:(UIButton*)button
{
NSLog(#"button pressed");
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, -100, 100, 100)];
view.backgroundColor = [UIColor blackColor];
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"Button" forState:UIControlStateNormal];
button.frame = CGRectMake(0, 10, 100, 20);
[view addSubview:button];
[self.view addSubview:view];
[UIView animateWithDuration:0.5 animations:^{
view.transform = CGAffineTransformMakeTranslation(0, 100);
}];
[view release];
}
I'm not sure that this was answered so I give it a shot, just for the record:
Check that the buttons are not outside the frame of any superview.
I found that a button placed outside like this may not work. Come to think about it, this is weird. I ran into this when I was making a view with buttons that animates in from below. Underneath this I have a gray touchable view to allow cancel. Only problem was that 1) for the gray area I used the parent view itself and 2) I let this gray area shrink as the subview animated in place ... so as a result, the buttons became outside, and it didn't work.
Solution was to leave the view full size and either add another as the gray, or make the first one gray and not shrunk (the only reason I wanted to avoid this was that it would make a pile of half transparent layers which is not optimal). Then the view with buttons on top of that. :)
Hope this helps.
If you have created the Button through programming then you have to do :-
myButton.userInteractionEnabled =YES;
Hope it Helps... :)

UIButton - changing frame alters response to touches

I have a UIButton. I found that when I moved it, it no longer responded to touches. Let me explain in detail how I determined this: when I shift a 100pixel width button by 50 pixels to the right, only the left half responded to my finger tapping it.
How do I tell the button "update your touches checker" when I dynamically change the frame of it? I am changing the frame in code, NOT in xib.
Initialization:
MATCGlossyButton *repeat = [[MATCGlossyButton alloc] init];
[repeat addTarget:self action:#selector(repeatPressed:) forControlEvents:UIControlEventTouchUpInside];
[repeat setTitle:#"Repeat" forState:UIControlStateNormal];
repeat.buttonColor = [UIColor brownColor];
repeat.backgroundColor = [UIColor clearColor];
repeat.cornerRadius = 20;
repeat.frame = CGRectMake(200,208,105,50);
And then later on:
repeat.frame = CGRectMake(265, 208, 105, 50);
Is it possible you are moving the button beyond the bounds of its parent view? Even if clipping is off, touches aren't propagated outside of a view.
An easy way to visualise the borders of your views is to do the folowing:
#import <QuartzCore/QuartzCore.h>
[...]
view.layer.borderWidth = 1;
view.layer.borderColor = [UIColor redColor].CGColor;

Adjusting button size in iPhone

I put a rounded-rect button into table-view footer.
But the button is streached to the left and to the right no matter how I change the BUTTON_WIDTH. But it's height is adjustable.
What's the problem? Below is the code I used.
#define BUTTON_WIDTH 80
#define BUTTON_HEIGHT 45
- (void)viewDidLoad {
CGRect frame = CGRectMake(0, 0, BUTTON_WIDTH, BUTTON_HEIGHT);
// btnSeeResult is decleared in header file
btnSeeResult = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnSeeResult setTitle:#"Result" forState:UIControlStateNormal];
[btnSeeResult addTarget:self action:#selector(seeResult) forControlEvents:UIControlEventTouchUpInside];
btnSeeResult.frame = frame;
self.tableView.tableFooterView = btnSeeResult;
}
It's because you're setting the button to be the tableView.tableFooterView, so that view's width will always be set to the width of the tableView.
Try using a black UIView as the footer view then adding the button to it, like this:
#define BUTTON_WIDTH 80
#define BUTTON_HEIGHT 45
- (void)viewDidLoad {
CGRect frame = CGRectMake(0, 0, BUTTON_WIDTH, BUTTON_HEIGHT);
// btnSeeResult is decleared in header file
UIButton *btnSeeResult = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnSeeResult setTitle:#"Result" forState:UIControlStateNormal];
[btnSeeResult addTarget:self action:#selector(seeResult) forControlEvents:UIControlEventTouchUpInside];
btnSeeResult.frame = frame;
// the width (320.0) actually doesn't matter here
UIView *footerView = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 80.0, 320.0)] autorelease];
footerView.backgroundColor = [UIColor clearColor];
[footerView addSubview:btnSeeResult];
self.tableView.tableFooterView = footerView;
}
#SeniorLee as per your question that you left in a comment-
The reason why it wasn't working when you were using the button as the footerview is because the footer view HAS to be the width of the table view. That is why when your button WAS your footerview, it was following the rule that it HAS to be the width of the table.
Now, when you use a regular old UIView, it takes the place of the footerview, and it's width is the same width as the table. Anything you add to that view, then, can be however you want it to be. Does that make sense?
The reason why the height could be changed is because the footerview's height doesn't have the same mandatory stipulations as the width does, so you can change the footer's height all you want, just not the width.

Adding multiple UIButtons to an UIView

I've added some buttons to an UIView (via addSubview) programmatically. However, they appear as overlays (so that I always see the last button only). How do I add new buttons below existing buttons?
Regards
you can offset the button like this
int newX = previousButton.frame.origin.x + previousButton.frame.size.width ;
int newY = previousButton.frame.origin.y ;
and either set the frame for new button when you create it:
[[UIButton alloc] initWithFrame:CGRectMake(newX,newY,100,100)];
or set the frame later
newButton.frame = CGRectMake(newX,newY,100,100);
Set the UIView's frame origin to layout the UIButtons in the locations you wish:
CGRect buttonFrame = button.frame;
buttonFrame.origin = CGPointMake(100.0f, 100.0f);
button.frame = buttonFrame;
view.addSubview(button);
You can either use the insertSubview:atIndex method or insertSubview:belowSubview of your view.
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(0,0,100,100)];
[myView insertSubview:myButton belowSubview:previousButton];
OR
[myView insertSubview:myButton atIndex:0];
Thanks for your answers guys.
I did the (horizontal) align with this code:
if([myContainer.subviews lastObject] == nil){
NSLog(#"NIL");
[myContainer insertSubview:roundedButton atIndex:0];
}else{
[myContainer insertSubview:roundedButton belowSubview:[tagsContainer.subviews lastObject]];
}
It works technically, but still overlays the buttons. I have to find a way, how to not overlay them...