I want to horizontally center a number of UIViews (they happen to be circles) in the master UIView. It will end up basically looking like the dots on the standard Page Control.
I have all the code written to create the circle UIViews I just have no idea how to arrange them horizontally and dynamically at run time.
Essentially I need some kind of horizontal container where I can do this
-(void)addCircle{
[self addSubView:[CircleView init]];
}
And it will auto arrange however many children it has in the center.
I get confused with auto-layout as well from time to time but here is a way how you can do it programmatically: (I assume that you add your circle views to a containerView property of your view controller and you do not add any other views to it.)
Add these two properties to your view controller:
#property (nonatomic) CGRect circleViewFrame;
#property (nonatomic) CGFloat delta;
Initiate those properties with the desired values in your view controller's viewDidLoad method:
// the size (frame) of your circle views
self.circleViewFrame = CGRectMake(0, 0, 10, 10);
// the horizontal distance between your circle views
self.delta = 10.0;
Now we add your "automatic addCircle method":
- (void)addCircleView {
UIView *newCircleView = [self createCircleView];
[self.containerView addSubview:newCircleView];
[self alignCircleViews];
}
Of course we need to implement the createCircleView method...
- (UIView*)createCircleView {
// Create your circle view here - I use a simple square view as an example
UIView *circleView = [[UIView alloc] initWithFrame:self.circleViewFrame];
// Set the backgroundColor to some solid color so you can see the view :)
circleView.backgroundColor = [UIColor redColor];
return circleView;
}
... and the alignCircleViews method:
- (void)alignCircleViews {
int numberOfSubviews = [self.containerView.subviews count];
CGFloat totalWidth = (numberOfSubviews * self.circleViewFrame.size.width) + (numberOfSubviews - 1) * self.delta;
CGFloat x = (self.containerView.frame.size.width / 2) - (totalWidth / 2);
for (int i = 0; i < numberOfSubviews; i++) {
UIView *circleView = self.containerView.subviews[i];
circleView.frame = CGRectMake(x,
self.circleViewFrame.origin.y,
self.circleViewFrame.size.width,
self.circleViewFrame.size.height);
x += self.circleViewFrame.size.width + self.delta;
}
}
This is the most important method which will automatically realign all your subviews each time a new circleView is added. The result will look like this:
Simple steps: append circle to container view, resize container view, center align container view
-(void)addToContanerView:(CircleView*)circle{
circle.rect.frame = CGrectMake(containers_end,container_y,no_change,no_change);
[containerView addSubview:circle];
[containerView sizeToFit];
containerView.center = self.view.center;
}
Assumptions:
containers_end & containers_y you can get from CGRectMax function,
for UIView SizeToFit method check here
To take care of rotation use make sure your Autoresizing subviews are set for left, right bottom and top margin.
You can try using this library. I have used it on several of my projects and so far, it worked really well.
https://github.com/davamale/DMHorizontalView
Related
I have a CustomScrollView which holds a list of UILabels and scrolls through them (the subclassing is for automatic positioning of the views). In the class I have a list of these views stored as an NSMutableArray. I have found that when I ask for label.view.frame.origin after getting the label from the array, I always get {0, 0}. Because of this, the logic I use to scroll does not work (the scroll is done programmatic-ally). I am trying to have the UILabel scroll to the center of the frame of the CustomScrollView. Here is a code sample to show where I am having problems:
#interface CustomScrollView : UIScrollView
#property (nonatomic) NSMutableArray* labels; //This is the array of UILabels
#end
#implementation CustomScrollView
-(void)scrollToIndex:(int)index withAnimation:(bool)animated
{
UILabel *label = (UILabel*)self.labels[index];
CGRect rect = CGRectMake(label.frame.origin.x - ((self.frame.size.width - label.frame.size.width) / 2), 0, self.frame.size.width, self.frame.size.height);
[self scrollRectToVisible:rect animated:animated];
}
#end
TL:DR - label.frame.origin.x is returning me 0 and not what it's relative position is within the superview.
Bonus - Whenever I instantiate my custom scroll view, it automatically adds two subviews which I have no idea where the come from. I set the background color and turn off the scroll bars and scroll enabled.
Thanks in advance for the help.
Edit [Jun 25 11:38] - Turns out the rect I am creating to scroll is correct, but calling [self scrollRectToVisible:rect animated:animated] is just not working.
Edit [Jun 25 12:04] - Here is the method which I call every time I add a subview
-(UILabel*)lastLabel
{
if (self.labels.count == 0)
return nil;
return (UILabel*)self.labels.lastObject;
}
-(void)adjustContentSize
{
if (self.labels.count > 0)
{
float lastModifier = [self lastLabel].frame.origin.x + [self lastLabel].frame.size.width + ((self.frame.size.width - [self lastLabel].frame.size.width) / 2);
self.contentSize = CGSizeMake(MAX(lastModifier, self.contentSize.width), self.frame.size.height);
}
}
Try using the convertRect function:
CGRect positionOfLabelInScrollView = [self.scrollView convertRect:myLabel.frame toView:nil];
[self.scrollView setContentOffset:CGPointMake(0, topOfLabel - positionOfLabelInScrollView.origin.y + positionOfLabelInScrollView.size.height) animated:YES];
This should scroll your scrollView to display the label.
so there must be something simple I am missing because I just can't figure out how to move my UIView
This is what I have so far
my .m file
- (void)viewDidLoad
{
[super viewDidLoad];
[self buttonLayout];
}
-(void)buttonLayout
{
UIView *theView = [self buttonGroup];
theView.center=CGPointMake(50, 50);
}
My .h file
#property (nonatomic, retain) IBOutlet UIView *buttonGroup;
So this is what I have so far and i just cant seem to get it to move at all
P.S i don't want it animated as i'm getting my layout to move to compensate for different iPhone screen sizes :)
I have not tested this, but it seems like you need to:
A) Remove the view from its parent.
B) Adjust the center of its 'frame', not the view.
C) Re-add the subview.
Like so:
UIView *theView = [self buttonGroup];
CGRect theFrame = theView.frame;
[theView removeFromSuperview];
theFrame.origin.x = 50.0;
theFrame.origin.y = 50.0;
theView.frame = theFrame;
[self.view addSubview:theView];
This should work with autolayout.
As the other poster says, you can't move views around by changing their center or frame when you have auto-layout in effect.
Instead, you have to add a vertical and horizontal constraint that positions the view from the top/left edge, connect an outlet to the constraint, and then change the constant value on the constraint from code.
I had similar problem , but when i tried to change the origin as suggested in first solution , i got "Expression not assignable" error.
so i solved it with below solution in viewDidLoad().
First save its center in some temp variable , then change the centre and add again to its parent view.
CGPoint buttonCenter = self.yourButton.center;
[self.yourButton removeFromSuperview];
buttonCenter.y = buttonCenter.y + 50.0;
self.yourButton.center = buttonCenter;
[self.view addSubview:self.yourButton];
I design the scrollview in interface builder like this
It looks good here. But unfortunately when I run it on emulator or device
it becomes
The content in scrollview is expand outside scrollview itself and even though outside UIView that contains this scrollView.
In my viewDidLoad (panel is the container of scrollView )
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
CGFloat adjustPanelHeight = [PTTScreenScaleUtil getAdjustHeight:self.panel.frame.size.height];
CGRect panelRect = self.panel.frame;
panelRect.size.height = adjustPanelHeight;
self.panel.frame = panelRect;
UIImage *image = [UIImage imageNamed:#"panel-background"];
UIGraphicsBeginImageContext(self.panel.frame.size);
[image drawInRect:CGRectMake(0, 0, self.panel.frame.size.width, adjustPanelHeight)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.panel setBackgroundColor:[UIColor colorWithPatternImage:newImage]];
NSLog(#"scrollView Height : %f", self.scrollView.frame.size.height);
NSLog(#"scrollView contentSize Height : %f", self.scrollView.contentSize.height);
// CGRect scrollViewRect = self.scrollView.frame;
// CGRect scrollViewContentRect = self.scrollView.frame;
// NSLog(#"ScrollView Height Before : %f , After : %f", self.scrollView.frame.size.height, [PTTScreenScaleUtil getAdjustHeight:self.scrollView.frame.size.height]);
// scrollViewRect.size.width = 280;
// scrollViewRect.size.height = [PTTScreenScaleUtil getAdjustHeight:270];
// self.scrollView.frame = scrollViewRect;
// [self.detailsLabel sizeToFit];
UIView *view = [[self.scrollView subviews] objectAtIndex:0];
// [view sizeToFit];
// [self.scrollView setContentMode:UIViewContentModeScaleAspectFit];
// NSLog(#"ContentSize Height : %f", view.frame.size.height);
// scrollViewContentRect.size.height = view.frame.size.height;
NSLog(#"Bounds : %f", view.bounds.size.height);
self.scrollView.frame = CGRectMake(10, 10, 280, 270);
self.scrollView.contentSize = CGSizeMake(280, 500);
NSLog(#"Frame Height %f", self.scrollView.frame.size.height);
//[self.scrollView setContentSize: CGSizeMake(280, 1000)];
CGRect termBtnRect = self.termBtn.frame;
CGRect mailBtnRect = self.mailBtn.frame;
CGRect twitterBtnRect = self.twitterBtn.frame;
CGRect fbBtnRect = self.fbBtn.frame;
termBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
mailBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
twitterBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
fbBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
self.termBtn.frame = termBtnRect;
self.mailBtn.frame = mailBtnRect;
self.twitterBtn.frame = twitterBtnRect;
self.fbBtn.frame = fbBtnRect;
}
All the log return 270.0
PS. the scroll bar is correct even though the content goes outside but the scroll bar is working correctly (stay in the scrollview's frame as arrange in interface builder)
I have no idea how can I solve this.
Anyone help me please.
Thanks you.
Solve it by creating new view controller in interface builder and redo the same process with careful and bingo. It works.
When I compare both two view controller I realise that the wrong one UIScrollView Clip Subviews is unchecked. When check it the problem solve.
I just struggled with this for an hour and had a head smack moment.
In my case, I had a UIView on the scene in the Storyboard. At some point I decided I needed it to be a UIScrollView instead (as opposed to the original plan which was to embed the UIScrollView in a UIView)
I went ahead and changed the class on the UIView to UIScrollView. IB changed it to Scroll View in the Document Outline, I figure I'm good, right?
And then I see the behavior you describe.
At some point it hits me that this isn't sufficient. Apparently adding a UIScrollView via IB does some things differently than adding a UIView and just changing class isn't enough. And this is probably the reason re-doing it from scratch fixed it for you.
So for anyone who runs into this in the future, make sure you added the UIScrollView via IB instead of a UIView
I was having the same problem as described in this post. I tried multiple combinations of solutions that did not work, including:
putting the scroll view inside a view with Clip To Bounds = YES
putting a view inside the scroll view with Clip To Bounds = YES, that then contained my child view
putting a Container View inside the scroll view, and then embedding my subview
rebuilding the Interface Builder files completely
every combination of autosizing mask options systematcially for both the scroll view and container view
clip to bounds enabled or disabled for every single element systematically
The child view in question had previously worked inside a scroll view, but wouldn't in this one case where the content blew outside the bounds of the scroll view.
In the end, I implemented the solution in code as I could find no way to get Interface Builder to co-operate:
// There are two scroll areas on the screen, the left view and the right view.
// We want the right view to contain a scrollable area with another child view controller
// we designed in Interface Builder.
// create a scroll view to fill the right view with a scrollable area
CGSize rightFrameSize = self.rightView.bounds.size;
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake (0, 0, rightFrameSize.width, rightFrameSize.height)];
scrollView.contentSize = CGSizeMake(640, 1352);
[self.rightView addSubview:scrollView];
// now create our child view controller from Interface Builder and add it to the scroll view
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"CustomerAddress" bundle:[NSBundle mainBundle]];
detailsView = [sb instantiateViewControllerWithIdentifier:#"CustomerDetailsView"];
detailsView.delegate = self;
detailsView.customer = _customer;
[scrollView addSubview:detailsView.view];
You could of course get the scrollView.contentSize from the child view controller you constructed in Interface Builder using scrollView.contentSize = detailsView.view.frame.size.
I have a love/hate relationship with Interface Builder... most days I love it, but some days we argue and I wish we'd never met... :)
only make cliptobound=YES in storyboard if you changed UIView to UIScrollView
So I have a UIScrollView and within that, I'd like to add some subviews, currently I have around 5 or 6, they are about 90px in height, so obviously when it gets to the bottom of the screen, I'd like it to scroll so you can see more, if that makes sense.
Below is what I have already, parentView being the UIScrollView, newResult being a row I'd like to append. GSResultBlock is a class that extends UIView.
for (NSObject * person in resultsCollection) {
CGRect resultBlockFrame = CGRectMake(0, resultCount, parentView.bounds.size.width, 90);
GSResultBlock * newResult = [[GSResultBlock alloc] initWithFrame:resultBlockFrame];
[parentView addSubview:[newResult getResultBlock]];
[newResult release];
resultCount = resultCount + 95;
}
Keep in mind that foreach newResult there will be additional subviews in there also, for example, a UIImageView ect.. (They are not included in the code above though)
Any ideas? Thanks in advance.
You need to update the contentSize of the UIScrollView using something like:
[parentView setContentSize:CGSizeMake(CGRectGetWidth(parentView.frame), resultCount)];
after the end of your for loop.
have a look at this answer
float maxHeight = 0;
for(UIView *v in [scrollView subviews]){
if(v.frame.origin.x + v.frame.size.height > maxHeight)
maxHeight = v.frame.origin.x + v.frame.size.height;
}
self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, maxHeight+5);
once you have put all your views into the scroll view, run this and it will make your scrollview scroll the right amount
I'm creating compatibility for iOS6 for an app made by someone else. I'm used to using making buttons/UI elements with autoresizing masks but I don't really know how they work when you're creating the button programatically.
For example:
- (UIButton*) createSampleButton {
UIButton* b = createSampleViewButton(CGRectMake(67, 270, 191, 45),
#"btn_shuffle",
#"btn_shuffle_active",
self,
#selector(sampleAction));
b.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
[self attachButton:b];
return b;
}
How can I change these buttons such that they'll be placed according to some scale/margin instead of arbitrarily choosing points until everything "looks right" ?
I was thinking of something like:
- (UIButton*) createSampleButton {
CGFloat height = self.bounds.size.height;
CGFloat bottomBound = 80;
UIButton* b = createSampleViewButton(CGRectMake(67, height-bottomBound, 191, 45),
#"btn_shuffle",
#"btn_shuffle_active",
self,
#selector(sampleAction));
[self attachButton:b];
return b;
}
This would guarantee me that the button is placed 80 points from the bottom of the screen every time right? Is there a more graceful or purposeful way of doing this?
The masks are the same as when created in IB or code. The thing you want to make sure to do in code though is make sure the frames are set properly proportioned once. In your case, yes you do want UIViewAutoResizingFlexibleTopMargin, and setting the correct y-value on the origin in terms of y = parentView.bounds.size.height - (x points as you described), is all you need to do.
EDIT:
According to your updated question, maybe this will help you. If the button has a constant size, set the frame to that size with CGPointZero as the origin when you create the button. If a UIView owns the button, then put this code in layoutSubviews. If a UIViewController owns the button, replace self.bounds with self.view.bounds and put this in view(Will/Did)LayoutSubviews (Assuming iOS5+).
// Aligning the button at it's current x value, current size, with its bottom border margin pizels from the bottom of the parent view.
CGFloat margin = 10;
CGRect buttonFrame = button.frame;
buttonFrame.origin.y = self.bounds.size.height - buttonFrame.size.height - margin;
button.frame = buttonFrame;
Also, define constant values at the top of the implementation file. Feel free to create convenience methods for readability (if you find this more readable and not doing too much on one line) such as
CGRect CGSetYInRect(CGFloat y, CGRect rect)
...
button.frame = CGSetYInRect(self.bounds.size.height - button.frame.size.height - margin, button.frame);
Use AutoResizing when appropriate to avoid explicit logic in layoutSubviews.
When you move to iOS 6 + only, use AutoLayout.