I have a UIView and I would like to move it up by 50.
I wrote the following:
NSLog(#"before %#",NSStringFromCGRect(_flixSuggestionsView.frame));
_flixSuggestionsView.frame = CGRectMake(_flixSuggestionsView.frame.origin.x, _flixSuggestionsView.frame.origin.y-45, _flixSuggestionsView.frame.size.width, _flixSuggestionsView.frame.size.height);
NSLog(#"after %#",NSStringFromCGRect(_flixSuggestionsView.frame));
The output is:
before {{0, 175}, {320, 324}}
after {{0, 130}, {320, 324}}
But the UIView stays in the same location.
What am I missing?
If any Autolayout constraints are defined for the view, these determine the view's
position (relative to other UI elements). If you want to control the position "manually",
you have to remove all constraints regarding this view, or switch off Autolayout completely
for the view controller.
Related
So after figuring out how scrollView works, I've implemented it with the following code:
self.scrollView.delegate = self;
self.scrollView.userInteractionEnabled = YES;
CGRect view = CGRectMake(0, 0, 320, 750);
self.scrollView.contentSize = view.size;
The above code works as intended on ALL simulators in Xcode 6. However, when I run it my phone (iphone4s on ios7), the scroll does not function at all. Are people experiencing the same problems since the new release? Or am I missing something I've learned from the documentation?
Had the same issue here. Just need to resize the scrollview's frame size in viewDidLayoutSubviews which overrides auto layout.
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[scrollView setContentSize:CGSizeMake(320, 2600)];
// Adjust frame for iPhone 4s
if (self.view.bounds.size.height == 480) {
scrollView.frame = CGRectMake(0, 0, 320, 436); // 436 allows 44 for navBar
}
}
In AutoLayout
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to the left edge of its superview, you’re really pinning it to the minimum x-value of the superview’s bounds. Changing the bounds origin of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.
The constraints on the subviews of the scroll view must result in a size to fill, which is then interpreted as the content size of the scroll view. (This should not be confused with the intrinsicContentSize method used for Auto Layout.) To size the scroll view’s frame with Auto Layout, constraints must either be explicit regarding the width and height of the scroll view, or the edges of the scroll view must be tied to views outside of its subtree.
Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints between the view and a view outside the scroll view’s subtree, such as the scroll view’s superview.
Here are two examples of how to configure the scroll view, first the mixed approach, and then the pure approach
Mixed Approach
Position and size your scroll view with constraints external to the scroll view—that is, the translatesAutoresizingMaskIntoConstraints property is set to NO.
Create a plain UIView content view for your scroll view that will be the size you want your content to have. Make it a subview of the scroll view but let it continue to translate the autoresizing mask into constraints:
- (void)viewDidLoad {
UIView *contentView;
contentView = [[UIView alloc] initWithFrame:CGRectMake(0,0,contentWidth,contentHeight)];
[scrollView addSubview:contentView];
// DON'T change contentView's translatesAutoresizingMaskIntoConstraints,
// which defaults to YES;
// Set the content size of the scroll view to match the size of the content view:
[scrollView setContentSize:CGSizeMake(contentWidth,contentHeight)];
/* the rest of your code here... */
}
Create the views you want to put inside the content view and configure their constraints so as to position them within the content view.
Alternatively, you can create a view subtree to go in the scroll view, set up your constraints, and call the systemLayoutSizeFittingSize: method (with the UILayoutFittingCompressedSize option) to find the size you want to use for your content view and the contentSize property of the scroll view
Pure Auto Layout Approach
To use the pure autolayout approach do the following:
Set translatesAutoresizingMaskIntoConstraints to NO on all views involved.
Position and size your scroll view with constraints external to the scroll view.
Use constraints to lay out the subviews within the scroll view, being sure that the constraints tie to all four edges of the scroll view and do not rely on the scroll view to get their size.
A simple example would be a large image view, which has an intrinsic content size derived from the size of the image. In the viewDidLoad method of your view controller, you would include code similar to the code shown in the listing below:
- (void)viewDidLoad {
UIScrollView *scrollView;
UIImageView *imageView;
NSDictionary *viewsDictionary;
// Create the scroll view and the image view.
scrollView = [[UIScrollView alloc] init];
imageView = [[UIImageView alloc] init];
// Add an image to the image view.
[imageView setImage:[UIImage imageNamed:"MyReallyBigImage"]];
// Add the scroll view to our view.
[self.view addSubview:scrollView];
// Add the image view to the scroll view.
[scrollView addSubview:imageView];
// Set the translatesAutoresizingMaskIntoConstraints to NO so that the views autoresizing mask is not translated into auto layout constraints.
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
// Set the constraints for the scroll view and the image view.
viewsDictionary = NSDictionaryOfVariableBindings(scrollView, imageView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]];
/* the rest of your code here... */
}
I did not try Vishu's answer, but what I did was update to iOS 8 so it's compatible with Xcode 6 and it worked!
My UIScrollView is a ~4500px horizontal view that the user needs to scroll horizontally through to view the content.
I have set it up as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
sview.frame = CGRectMake(0, 0, 568, 320);
sview.contentSize = CGSizeMake(4500, 320);
[sview setScrollEnabled:YES];
}
Yet the scroll view does nothing. Is there something obvious I missed? i've tried literally every tutorial on the web.
I got similar issue. I did following modifications and the scrollView started scrolling for me:
Select to check the 'Bounce Horizontally' property for UIScrollView
in xib.
Move the code following code to viewDidAppear instead of
viewDidLoad:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
sview.frame = CGRectMake(0, 0, 568, 320);
sview.contentSize = CGSizeMake(4500, 320);
[sview setScrollEnabled:YES];
}
I think this should help you.
I've explained it here, but there are so many answers to this problem that suggests turning off Auto Layout. That fixes the problem but that's not really the correct solution. Here's my answer:
Turning Auto Layout works, but that's not the solution. If you really need Auto Layout, then use it, if you don't need it, turn it off. But that is not the correct fix for this solution.
UIScrollView works differently with other views in Auto Layout. Here is Apple's release note on Auto Layout, I've copied the interesting bit:
Here are some notes regarding Auto Layout support for UIScrollView:
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left, bottom,
and right edges within a scroll view now mean the edges of its content
view.
The constraints on the subviews of the scroll view must result in a size to fill, which is then interpreted as the content size of the
scroll view. (This should not be confused with the
intrinsicContentSize method used for Auto Layout.) To size the scroll
view’s frame with Auto Layout, constraints must either be explicit
regarding the width and height of the scroll view, or the edges of the
scroll view must be tied to views outside of its subtree.
Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints
between the view and a view outside the scroll view’s subtree, such as
the scroll view’s superview.
Apple then goes on to show example of how to correctly use UIScrollView with Auto Layout.
As a general rule, one of the easiest fix is to create a constraint between the element to the bottom of the UIScrollView. So in the element that you want to be at the bottom of the UIScrollView, create this bottom space constraint:
Once again, if you do not want to use Auto Layout, then turn it off. You can then set the contentSize the usual way. But what you should understand is that this is an intended behaviour of Auto Layout.
First of all you have to add some content to UIScrollSiew as subview for scrolling,without content on UIScrollView how can you scroll?. Here is what i did,just add UIImageView to UIScrollView as subview of size same as size of UIScrollView...
In viewDidLoad method try the following code..
-(void)viewDidLoad
{
UIScrollView *scroll=[[UIScrollView alloc] init];
scroll.frame=CGRectMake(0, 0, 320, 460);
UIImageView *imageView=[[UIImageView alloc] init];
imageView.frame=CGRectMake(0, 0, 320,460);
imageView.image=[UIImage imageNamed:#"chiranjeevi.jpeg"];
scroll.contentSize = CGSizeMake(4500, 460);
[scroll setScrollEnabled:YES];
[scroll addSubview:imageView];
[self.view addSubview:scroll];
}
I tested this code it works well.I hope this code will be helpful to you..
I assume you are adding UISrollingView in your Xib file. This will work for you.
sview.delegate = self;
sview.backgroundColor=[UIColor clearColor];
[sview setCanCancelContentTouches:NO];
sview.indicatorStyle = UIScrollViewIndicatorStyleWhite;
sview.clipsToBounds = YES;
sview.scrollEnabled = YES;
sview.contentSize = CGSizeMake(320,570);
CGPoint topOffset = CGPointMake(0,0);
[sview setContentOffset:topOffset animated:YES];
Also, make sure to give IBOutlet connection in your Xib file.
I also faced the same issue.I added the scroll view in xib.I also added some subviews to this scroll view. The scroll view would stop scrolling after I added the subviews. The solution for this problem was in the xib for the view in file inspector Use Autolayout was checked. I unchecked it and the scroll view scrolled after adding the subviews.
The solution was uncheking the Use Autolayout in file inspector in xib.
I have a custom UIView which I set to the tableHeaderView property of a UITableView during loadView:
headerView = [[MYViewClass alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 120)];
[headerView sizeToFit];
self.tableView.tableHeaderView = headerView;
The view draws incorrectly, with part of the view clipped and white space above the table. When rotating the device the view almost disappears. On rotating back the view is now bigger that the space for the header and obscures some of the cells in the table.
To troubleshoot I have overrode the setFrame method in my custom view class:
- (void) setFrame:(CGRect)frame {
[super setFrame:frame];
NSLog(#"%# - %#", NSStringFromSelector(_cmd), NSStringFromCGRect(frame));
}
I also put a break point on the NSLog statement so I could see what called the setFrame and I get some odd results that I can't explain and hope someone can shed some light on what is happening and why.
During loadView
1. initWithFrame calls: setFrame: - {{0, 0}, {320, 120}}
2. sizeToFit calls: setFrame: - {{0, 0}, {320, 109}}
3. setTableHeaderView calls: setFrame: - {{0, 0}, {320, 109}}
4. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {320, 109}}
5. _resizeWithOldSuperviewSize calls: setFrame: - {{0, 0}, {320, 65}}
6. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {320, 65}}
7. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {320, 65}}
Rotating the device left
1. _resizeWithOldSuperviewSize calls: setFrame: - {{0, 0}, {480, 0}}
2. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {480, 0}}
3. _resizeWithOldSuperviewSize calls: setFrame: - {{0, 0}, {480, 12}}
4. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {480, 12}}
Rotating the device right
1. _resizeWithOldSuperviewSize calls: setFrame: - {{0, 0}, {320, 172}}
2. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {320, 172}}
3. _resizeWithOldSuperviewSize calls: setFrame: - {{0, 0}, {320, 160}}
4. _adjustTableHeaderAndFooterViews calls: setFrame: - {{0, 0}, {320, 160}}
Which explains why my view looks truncated initially, then almost disappears and finally ends up overlapping the cells. It appears that _resizeWithOldSuperviewSize is the culprit and what I don't understand is why it is being called and where it is getting these odd values from.
I have a really rubbish work around by calling [self.tableView.tableHeaderView sizeToFit] in viewDidAppear: and didRotateFromInterfaceOrientation: which puts the frame back to the correct size but the redrawing is awful as both happen after the view is visible or after the rotation animation. Trying to set this anytime before the view is visible causes _resizeWithOldSuperViewSize to set the frame back to these odd sizes.
You need to get the intrinsic size and autoresizingMask right. Once you do that, UITableView will stop screwing with your tableHeaderView.
For example, if you want your header to have a fixed height which depends on its contents, but you want your header to use the entire table width, this is what you would do:
1.Create your view with the following Autoresizing mask configuration (so the table won't try to increase the header view height):
2. Add your subviews using AutoLayout so the top/bottom anchors match your view (this is so your subviews define the height of your header view)
3. Add your header view like so:
let headerView : CustomView? = CustomView.fromNib()
headerView?.sizeToFit()
tableView.tableHeaderView = headerView
It will help you to read up on Apple's view resizing scheme through the autoresizingMask property of UIView. Possible values are:
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;
I have no doubt that after some experimentation you will find the right resizing mask combination to solve your problem.
Also, please note that sizeToFit, according to the same documentation source "resizes and moves the receiver view so it just encloses its subviews." It seems inappropriate to call that just after creating the view and before putting it into the table header.
Got totally the same problem when I wanted to have table header view with SearchBar and some custom UI at the bottom. The funny thing that if you keep only SearchBar it works just perfect.
So I decided to apply a really dirty hack, reseting Frame's Height in the overridden Frame setter of your custom UIView (there you're logging frame size) to something you need (in your case it's 120).
- (void) setFrame:(CGRect)frame {
frame.Height = 120;
[super setFrame:frame];
}
After that there is no weird header view and table overlapping on rotation. Hope it helps.
P.S. BTW I use Auto Layout inside my custom UIView.
Create a new xib file and then customize your preferred size in the xib file. Say the xib is called customHeader (.xib) Declare:
IBOutlet UIView *customView;
Assign customView to that view in your xib.
-(UIView *)viewOfCategory;
In .m:
-(UIView *)viewOfCategory {
if (!customView) {
[[NSBundle mainBundle] loadNibNamed:#"customHeader" owner:self options:nil];
}
return customView;
}
Then call this UITableView method:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection: (NSInteger)section {
return [self customView];
}
THEN FINALLY AND MOST IMPORTANT THAT WILL ANSWER YOUR QUESTION! Implement this other UITableView method:
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
// You can just return default size of your xib: return [[self viewOfCategory] bounds].size.height;
// Or you can return custom size using CGRect or whatever.
}
I have an iPad app and I'm having an issue in my rootViewController. I'm getting the wrong frame for it for some reason. The rotation works fine visually, but when I'm in landscape mode my width and height are reversed. Logging the frame I get the following:
Portrait: self view frame = {{0, 20}, {768, 1004}}
Landscape: self view frame = {{20, 0}, {748, 1024}}
That 20px for the status bar is moving as it should. But those values should be flipped. This view has a UIScrollView as a subview and it's frame is adjusting as it should. Has anyone come across this before?
If the width and height were reversed, the origin would then be wrong... I think this is the expected behaviour, as these coordinates are in the super view's system. What do you get with the bounds rectangle?
I have a view hierarchy like this:
UIView
- ADBannerView
- UIImageView
- UILabel
- UIButton
- UINavigationController
- UIView
I'm loading the image view, label and button from a nib file and the UINavigationController from another nib. All have autoresizing masks set. I'm creating the ADBannerView programmatically.
Now my problem is that I would like the image, label, button to move down and the navigation controller to shrink when I insert an ADBannerView. However this is not happening, instead the ADBannerView is placed on top of the image and the label.
Can anybody explain to me what am I doing wrong here?
In other to get those things to "automatically" shift down when you put in the ADBannerView, you'll need to enclose them in their own view and then change the size and position of that view. Assuming the ADBannerView is 50 pixels tall, you'll want to move that UIView down 50 pixels and reduce its height by 50 pixels.
Assuming that self.enclosingView is the new view that you will use to enclose the image, label and button... and assuming you want to make this animated (you probably do, it usually looks a lot better):
// Start the AdBannerView off of the top of the screen
CGRect adFrame = self.bannerView.frame;
adFrame.origin.x = 0.0;
adFrame.origin.y = 0.0 - adFrame.size.height;
self.bannerView.frame = adFrame;
[UIView beginAnimations:#"Show Ads" context:nil];
// Animate the shrinking of the enclosing view
CGRect enclosingFrame = self.enclosingView.frame;
enclosingFrame.size.height -= self.bannerView.frame.size.height;
enclosingFrame.origin.y += self.bannerView.frame.size.height;
self.enclosingView.frame = enclosingFrame;
// Animate the motion of the bannerView into view
adFrame.origin.y = 0.0;
self.bannerView.frame = adFrame;
[UIView commitAnimations];
Autoresizing mask defines size changes on parent's frame changes, not on sibling insertion/removal. You have to adjust frame for corresponding views programmatically. Kenny Wyland already gave you idea how this can be achieved with less pain. Take a look at CGRectDivide method - with it it's easy to split available space between two views.