I should know this but don't and can't find it explained anywhere.
I am moving a UIView in the coordinate space of the window and would like its subview (a tableView) added in code to move as well. I have not added any explicit constraints linking the subview to its superview thinking they would move in tandem. The tableview is not moving, however, as far as I can tell, when I move the superview.
Is it normal behavior for a subview created in code to be unaffected by changing the coordinates of its superview? If so, do you have to add constraints in code, should you manually move the subviews at the same time you are moving the superview or how can you get the subview to move in tandem? Here is code:
//Create view and subview (tableView):
myView= [UIView new];
CGFloat width = self.view.frame.size.width;
CGFloat height=self.tableView.frame.size.height;
//Place offscreen
[myView setFrame:CGRectMake(-width, 0, width, height)];
[self.view addSubview:myView];
aTableView = [UITableView new];
//Initially set frame to superview
aTableView.frame = myView.frame;
[myView addSubview:aTableView];
//Move superview on screen
myRect = CGRectMake(0,0,width,height)];
myView.frame = myRect;
myView moves but Tableview does not seem to move from this alone. How can I move it?
I'm assuming you say "myView moves but Tableview does not seem to move" because you don't see Tableview on-screen? If so, it looks like it's due to the way you set the frame(s).
//Create view and subview (tableView):
myView= [UIView new];
CGFloat width = self.view.frame.size.width;
CGFloat height=self.tableView.frame.size.height;
//Place offscreen
[myView setFrame:CGRectMake(-width, 0, width, height)];
[self.view addSubview:myView];
OK - myView is now off-screen to the left. Let's assume a width of 320 and a height of 480, so your myView's frame is (for example):
`-320, 0, 320, 480`
then
aTableView = [UITableView new];
//Initially set frame to superview
aTableView.frame = myView.frame;
[myView addSubview:aTableView];
Whoops, you set aTableView.frame = myView.frame; That means your table's frame is:
`-320, 0, 320, 480`
but that is relative to myView's frame. So your table view is located 320-ptd to the left of myView, which comes out to 640-pts to the left of the left edge of the screen.
//Move superview on screen
myRect = CGRectMake(0,0,width,height)];
myView.frame = myRect;
Now you've moved myView's left to 0, so it's visible, but aTableView is still 320-pts to the left of myView, so it's still off-screen.
Changing that one line to:
aTableView.frame = myView.bounds
should take care of it.
Related
Im practicing objective-C, and I try to do everything programmatically.
I'm making a simple view that I add on my view of the ViewController, but this subview is going out of the screen.
When I set my frame, the position for the X and Y are respected, but the rest, no...
Here is the screenshot of my result :
As you can see... The red subview is going out of the screen.
Here is my loadView where I add that subview :
HomeViewController.m - loadView
-(void)loadView
{
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.frame.size.width - 30, self.view.frame.size.height - 30)];
[subview setBackgroundColor:[UIColor redColor]];
[self.view addSubview:subview];
}
For the padding, I did put 15 for the position x and y... And for the frame, I did calculate with the size of the self.view by removing paddings... As you see, it works well for the width, but for the height, it is a big fail. It goes outside the screen.
In my AppDelegate.h, I set the navigationController.navigationBar.translucent = NO;, in order to that when I set position for x, and y, it starts well after the navigationBar .
I don't understand this weird behavior for the height... If someone has a good explanation for this please.
Thanks
First, you shouldn't rely on the value of self.view in viewDidLoad. It is set to a correct value later, in viewWillAppear:. You can keep your code, if you make your subview resize automatically when self.view is displayed. For that, set autoresizingMask on the subview:
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
(or add an equivalent set of constraints if you use Auto Layout.)
Also, I recommend to use bounds instead of frame:
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.bounds.size.width - 30, self.view.bounds.size.height - 30)];
It doesn't make a difference here, but it often does, e.g. if you calculate the frame's x and y based on the parent frame.
loadView method just creates the view. At the point when loadView gets called there is no information about final view frame hence its children views cannot be placed properly.
The right place to update your children views' frames is viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// update child view frame here
}
Remarks: you can define auto-layout constraints of your child view in code and they will be automatically applied to child views when view controller's view gets resized.
Consider the following UIView "MainView":
The view includes a Container View which in turn houses a UITableView controller. The container view's y coordinate starts just beneath the gradient bar. The UITableView includes the section footer at very bottom with the 'STU' label and 'chart' button.
When the UIView loads, and up-to-and-until any interaction with the tableView, MainView's dimensions are:
Frame: 0.000000x, 0.000000y, 568.000000w, 268.000000h
I have a delegate protocol set up such that tapping the chart button in the tableView will create a new view in MainView for a shadow effect via a method performing:
CGRect newFrame = self.view.frame; // self = MainView
newFrame.size.width = 100;
newFrame.size.height = 50;
UIView *backgroundShadowView = [[UIView alloc] initWithFrame:newFrame];
backgroundShadowView.backgroundColor = [UIColor blackColor];
// Do Animation
The important part above is the 'newFrame' CGRect. For some reason after interacting with the table view by tapping the chart button, or even scrolling or tapping a row, self.view.frame suddenly has the following dimensions:
Frame: 0.000000x, 52.000000y, 568.000000w, 268.000000h
And so the shadow view appears as follows, with a y origin much farther down than where it would be expected to start, just above the gradient bar.
I've adjusted the width and height of the "shadowview" for this question; normally it would be 568x268, but would extend 52 units off screen on the bottom because of this issue.
52 units is exactly the height of the statusbar (20) + navigationbar_in_landscape (32).
Of course I could manually adjust the frame dimensions, but I do not want to. I want to know why the view's frame is changing unexpectedly.
For the life of me, I cannot figure out why the view becomes suddenly offset. Any help is appreciated!!
Two comments.
(1)
This code was probably always wrong:
CGRect newFrame = self.view.frame; // self = MainView
newFrame.size.width = 100;
newFrame.size.height = 50;
UIView *backgroundShadowView = [[UIView alloc] initWithFrame:newFrame];
You surely want to define backgroundShadowView's frame in terms of self.view's bounds, not its frame as you are doing in the first line here.
(2)
The change in self.view.frame is probably illusory. You are probably checking this initially in viewDidLoad. But that is too soon; the view has not yet been added to the interface, and so it has not yet been resized to fit the surroundings.
To set a UIView to take up the entire screen, is this correct?
self.mapView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
Or should I be using the self.view.bounds...?
You can use either of frame and bounds for view as frame and bounds are same for it. But when you work with subviews then use bounds because frame and bounds need not be the same. For subview they are same only when your subview's size is same as its superview.
I faced a strange problem, the scrollview does not scroll down, only scroll up. I have scrollview in my app, please look at my coding
.....
self.scrollView = [[UIScrollView alloc] initWithFrame: CGRectMake(0, 0, 320,427)];
[self.view addSubViews: self.scrollView];
UIView *blueView = [[UIView alloc] initWithFrame: CGRectMake(0, 47, 320, 320)];
blueView.backgroundColor = [UIColor blueColor];
[self.scrollView addSubViews: blueView];
self.scrollView.contentSize = CGSize(320, 640);
....
My problem is no matter what value I changed contentSize, my ScrollView only scroll up, not scroll down. I want user can move blueView to the top or bottom of iPhone screen from the original position.
do you have this problem?
The Problem
It looks like your issue is with how you're orienting blueView within scrollView. You're setting the frame of blueView to the CGRect (0, 47, 320, 320). When you set the frame like this, one of the things you're implicitly saying is:
The top edge of blueView is 47 points below the top edge of scrollView.
That's a perfectly valid thing to say, but it's what's causing the problem you describe. scrollView won't scroll down because it is designed to start, by default, with the rect (0, 0, 320, 480) in view. The contentSize property only indicates the size of the content within the UIScrollView, not its positioning. When you set it, you're basically telling scrollView:
Starting from your content origin, the content is 320 points wide and 640 points tall.
Thus, scrollView won't scroll up because, as far as it knows, there's no content above the coordinate (0, 0).
The Solution
There are three steps you'll need to take to get the functionality you want.
Set the contentSize to be just big enough to allow blueView to scroll all the way up and down.
Put blueView in the vertical center of scrollView.
Scroll the scrollView so that it is initially centered on blueView.
Set the contentSize to be just big enough to allow blueView to scroll all the way up and down.
We'll want to calculate the correct value of the contentSize property. It is of the type CGSize, so we need two parts: width and height. width is easy – since you don't seem to want horizontal scrolling, just make it the width of the screen, 320. Height is a little more tricky. If you want blueView to just touch the top and bottom of the screen when scrolled up or down, you need to do some math. The correct total height will be double the height of the screen, minus the height of blueView. So:
scrollView.contentSize = CGSizeMake(320, 480 * 2.0 - blueView.frame.size.height);
Put blueView in the vertical center of scrollView.
That's easy; just set the center property of blueView:
blueView.center = CGPointMake(160, scrollView.contentSize.height / 2.0);
Scroll the scrollView so that it is initially centered on blueView.
If you check the Apple UIScrollView documentation, you'll see an instance method - (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated. This is exactly what you need to scroll scrollView programmatically. The rect you want is the one centered on blueView, with the size of the iPhone screen. So:
CGRect targetRect = CGRectMake(0, scrollView.contentSize.height / 2.0 - 240,
320, 480);
[scrollView scrollRectToVisible:targetRect animated:NO];
Make sure you do this scrolling in viewWillAppear, so it's ready right when the user sees the view.
That should be it. Let me know if you have any questions!
The content size of the scrollView should be the size of the view it is holding. This is how the code should be, try something like this.
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(X, Y, W, H1)];
UIView * blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, W, H2)];
self.scrollView .contentSize = blueView.frame.size;
[self.scrollView addSubview:blueView];
[self.view addSubView: self.scrollView];
Thanks to Riley. Here, the H1 is the height of the UIScrollVIew and H2 is the height of the blueView and (H1 < H2).
I have a parent UIView that has child UIView (UILabel used in the code below) whose frame is set to the parent's bounds and whose autoresizingMask is set to flexible width and height:
UIView* parentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
UILabel* childLabel = [[UILabel alloc] initWithFrame:parentView.bounds];
childLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
childLabel.textAlignment = UITextAlignmentCenter;
childLabel.text = #"Hello";
I want to be able to animate the parent view's frame, specifically its size, and have the subview resize as part of the animation:
[UIView animateWithDuration:1.0 animations:^{ parentView.frame = CGRectMake(0, 0, 160, 240); }];
As a result of this animation I would want the text of the UILabel to animate along with the parent view's animation, so visually you'd see the text move from being centered at (160, 240) to (80, 120). However, instead of animating it appears the subview's frame is being immediately set to the value it should have at the end of the animation, so you see the position of the text immediately jump when the animation starts.
Is there a way to get subviews to autoresize as part of an animation?
I don't completely have my head around what's going on, but I think the core issue is that UIKit doesn't want to have to re-render the text every frame of the animation, so the contents of a UILabel aren't animatable. By default, the contentMode property of UILabel is UIViewContentModeRedraw, meaning that it'll redraw the UILabel at the target size as soon as the property is set.
If you change the contentMode to UIViewContentModeCenter, the contents won't redraw and will remain centered in the UILabel.
childLabel.contentMode = UIViewContentModeCenter;