UIScrollView, paging and rotation: Second view is not aligned properly after rotation - objective-c

I am using a UIScrollView with Paging enabled and the following code to add subviews (core plot charts) to it.
The horizontal scrolling between the views works properly.
However, when showing the second view and then rotating from landscape to portrait mode, the second view is shifted partly to the right and a portion of the first view's right hand side is shown on the left side, hence "destroying" the paging mode.
Could you help me with these issue please? I tried many alternatives, but can't find my bug. Thank you so much!
This is how my iPad screen looks after rotating to portrait mode with the second view:
:
This is my viewDidLoad method:
- (void)viewDidLoad {
self.scrollView.contentSize = CGSizeMake(768 * 2, 400);
chart1 = [[CPTGraphHostingView alloc]initWithFrame:CGRectMake(0, 0, 768, 400)];
chart2 = [[CPTGraphHostingView alloc]initWithFrame:CGRectMake(768, 0, 768, 400)];
self.scrollView.autoresizesSubviews = NO;
chart1.autoresizesSubviews = YES;
chart2.autoresizesSubviews = YES;
self.scrollView.contentOffset = CGPointZero;
self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth ;
chart1.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth ;
chart2.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth ;
[self.scrollView addSubview:chart1];
[self.scrollView addSubview:chart2];
}
This is how I have implemented rotation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (UIDeviceOrientationIsPortrait(fromInterfaceOrientation)) {
self.scrollView.contentSize = CGSizeMake(704 * 2, 400);
chart1.frame = CGRectMake(0, 0, 704, 400);
chart2.frame = CGRectMake(704, 0, 704, 400);
}
else {
self.scrollView.contentSize = CGSizeMake(768 * 2, 400);
chart1.frame = CGRectMake(0, 0, 768, 400);
chart2.frame = CGRectMake(768, 0, 768, 400);
}
}

I think you should change the contentOffset of the scrollview when rotation is taking place.
You should have a way to know which page is currently displayed before rotation (maybe put this information in a variable). Then in your didRotate.. method set the contentOffset of the scrollview after resizing it, like this:
CGFloat offset = self.scrollView.frame.size.width * currentPageIndex;
[self.scrollView setContentOffset: offset];

As an alternative to laying out your subviews in your view controller, have you considered subclassing your UIScrollView and overriding it's layoutSubviews method? You might also consider defining your dimensions as percentages rather than fixed points - because the point values will shift according to rotation and presence of other UI elements such as navigation and toolbars. You may run into trouble as you're manually resizing UI elements in your rotation method, at the same time that the UI is going to be attempting to automatically resize elements according to your resizing masks. Just my thought...

Related

Hide and show UIView with bottom View filling the space when hidden using autolayout

I have two views on top of one another like so. When I press button, I want to hide the grey view and have the pink view move up into its space. When I press button again, I want the grey view to re-appear and move the pink view back into its original position.
I can accomplish this by doing something like
- (IBAction)toggle:(id)sender {
if (self.top.hidden) {
self.top.hidden = NO;
self.top.layer.frame = CGRectMake(0, 0, 320, 50);
self.bottom.layer.frame = CGRectMake(0, 50, 320, 50);
} else {
self.top.layer.frame = CGRectMake(0, 0, 0, 0);
self.bottom.layer.frame = CGRectMake(0, 0, 320, 50);
self.top.hidden = TRUE;
}
}
However, the general wisdom, from what I understand, regarding autolayout is to not set the frame sizes programmatically in the code. So my question is, how can achieve the same result using autolayout?
It's basically the iPhone version of this question: OS X Cocoa Auto Layout hidden elements
Found the answer courtesy of this question: Animating an image view to slide upwards
In short:
- (IBAction)toggle:(id)sender {
if (self.top.hidden) {
self.top.hidden = NO;
[UIView animateWithDuration:0
animations:^{
self.verticalSpace.constant += 50.0;
[self.view layoutIfNeeded];
}];
} else {
[UIView animateWithDuration:0
animations:^{
self.verticalSpace.constant -= 50.0;
[self.view layoutIfNeeded];
}];
self.top.hidden = TRUE;
}
}
where self.verticalSpace is the constraint set between the bottom view to the top view.

setting view boundaries

I have a scrollview with an image as a subview. I would like to set the boundaries of the scrollview to be the size of the image view, so that you wouldn't be able to see any of the background.
I don't want this happening anymore.
The weird part is, that after you zoom in or out on the image, then the boundaries seem to fix themselves, and you can no longer move the image out of the way and see the background.
This is what I have going for code:
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
// return which subview we want to zoom
return self.imageView;
}
-(void)viewDidLoad{
[super viewDidLoad];
[self sendLogMessage:#"Second View Controller Loaded"];
//sets the initial view to scale to fit the screen
self.imageView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
//setes the scrollview's delegate to itself
self.scrollView.delegate = self;
//sets the maximum zoom to 2.0, meaning that the picture can only become a maximum of twice as big
[self.scrollView setMaximumZoomScale : 2.5];
//sets the minimum zoom to 1.0 so that the scrollview can never be smaller than the image (no matter how far in/out we're zoomed)
[self.scrollView setMinimumZoomScale : 1.0];
[imageView addSubview:button];
}
I thought that this line would solve my problem
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
But like I said, it only works after I zoom in or out.
EDIT: When I switch
self.scrollView.contentSize = self.imageView.image.size;
to
self.scrollView.frame = self.imageView.frame;
It works like I want it to (you can't see the background), except the toolbar on the top is covered by the image.
imageView.image.size isn't necessarily the frame of the imageView itself, try setting the
scrollview.frame = imageView.frame
and then
scrollView.contentSize = imageView.image.size
Then you won't see any border. If you want the image to be the maximum size to start with,
do
imageView.frame = image.size;
[imageView setImage:image];
scrollView.frame = self.view.frame; //or desired size
[scrollView addSubView:imageView];
[scrollView setContentSize:image.size]; //or imageView.frame.size
To fix this, I ended up declaring a new CGRect , setting its origin to my scrollView's origin, setting its size with the bounds of my view, and then assigning this CGRect back to my scrollview frame
CGRect scrollFrame;
scrollFrame.origin = self.scrollView.frame.origin;
scrollFrame.size = CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
self.scrollView.frame = scrollFrame;

UIScrollView Isn't Scrolling

Every time this question gets asked on SO (and elsewhere) the answer is "make sure the contentSize is larger than the view size, and set it in viewDidLoad!" Yes, I'm doing that, so what else could be the problem?
In my controller's loadView: I have these lines to set up my view:
descriptionView = [[DetailDescriptionView alloc] initWithFrame:CGRectMake(160, 60, 160, 180)];
descriptionScrollView = [[DetailDescriptionScrollView alloc] initWithFrame:CGRectInset(CGRectMake(0, 0, 160, 180), 5, 5)];
[descriptionView addSubview:descriptionScrollView];
[self.view addSubview:descriptionView];
Then, in viewDidLoad, I have this code:
[descriptionScrollView setDescriptionText:#"<lots of text that will require scrolling>"];
NSUInteger contentHeight = <** calculations on the text to determine height **>
[descriptionScrollView setContentSize:CGSizeMake(descriptionScrollView.frame.size.width, contentHeight)];
NSLog(#"ContentSize: %#", NSStringFromCGSize(descriptionScrollView.contentSize));
NSLog(#"DescriptionScrollView Size: %#", NSStringFromCGRect(descriptionScrollView.frame));
Those NSLogs confirm all should be fine:
ContentSize: {150, 330}
DescriptionScrollView Size: {{5, 5}, {150, 170}}
When it comes to doing the actual drawing of the text, I'm using Core Graphics in the scrollview's drawRect: method. Here's the relevant snippet:
CGRect descriptionTextBox = CGRectMake(self.frame.origin.x, 20, self.frame.size.width - 10, 330);
[self.descriptionText drawInRect:descriptionTextBox withFont:[UIFont fontWithName:kMediumFontName size:kFontSizeMicro]];
I think that covers what I'm trying to do. The scrolling just doesn't occur. Can anyone recommend a tactic to figure out what to try to make this work?
You're drawing in the scrollview's draw rect; it needs to be in a subview of the scrollview.

Programmatically resize NSView

I have a main view that is supposed to display several subviews. Those subviews are directly above and below one another (in the z axis) and will drop down (in the y axis) and move up using this code:
if (!CGRectIsNull(rectIntersection)) {
CGRect newFrame = CGRectOffset (rectIntersection, 0, -2);
[backgroundView setFrame:newFrame];
} else{
[viewsUpdater invalidate];
viewsUpdater = nil;
}
rectIntersection is used to tell when the view has entirely moved down and is no longer behind the front one (when they no longer overlap rectIntersection is null), it moves down by 2 pixels at a time because this is all inside a repeating timer. I want my main view, the one that contains these two other views, to resize downward so that it expands just as the view in the background is being lowered. This is the code I'm trying for that:
CGRect mainViewFrame = [mainView frame];
if (!CGRectContainsRect(mainViewFrame, backgroundFrame)) {
CGRect newMainViewFrame = CGRectMake(0,
0,
mainViewFrame.size.width,
(mainViewFrame.size.height + 2));
[mainView setFrame:newMainViewFrame];
}
The idea is to check if the mainView contains this background view. When the backgroundView is lowered, the main view no longer contains it, and it (should) expand downward by 2 pixels. This would happen until the background view stopped moving and the mainView finally contains backgroundView.
The problem is that the mainView is not resizing at all. the background view is being lowered, and I can see it until it disappears off the bottom of the mainView. mainView should have resized but it does not change in any direction. I tried using setFrame and setBounds (with and without setNeedsDisplay) but nothing worked.
I'm really just looking for a way to programmatically change the size of the main view.
I think I understood, what the problem is. I read carefuly the code.
if (!CGRectIsNull(rectIntersection)) {
// here you set the wrong frame
//CGRect newFrame = CGRectOffset (rectIntersection, 0, -2);
CGRect newFrame = CGRectOffset (backgroundView.frame, 0, -2);
[backgroundView setFrame:newFrame];
} else{
[viewsUpdater invalidate];
viewsUpdater = nil;
}
rectIntersection is actually the intersection of the two views, that overlap, and as the backgroundView is moved downward, that rect's height decreases.
That way the mainView gets resized only one time.
To add on this, here is a simple solution using block syntax, to animate your views, this code would typically take place in your custom view controller.
// eventually a control action method, pass nil for direct call
-(void)performBackgroundViewAnimation:(id)sender {
// first, double the mainView's frame height
CGFrame newFrame = CGRectMake(mainView.frame.origin.x,
mainView.frame.origin.y,
mainView.frame.size.width,
mainView.frame.size.height*2);
// then get the backgroundView's destination rect
CGFrame newBVFrame = CGRectOffset(backgroundView.frame,
0,
-(backgroundView.frame.size.height));
// run the animation
[UIView animateWithDuration:1.0
animations:^{
mainView.frame = newFrame;
backgroundView.frame = newBVFrame;
}
];
}

iPhone SDK UIScrollView doesn't get touch events after moving it

I'm subclassing UIScrollView and on the start I fill this ShowsScrollView with some items. After filling it, I setup frame and contentSize to this ShowsScrollView. Everything works fine for now, i get touches events, scrolling is working..
But after rotation to landscape, I change x and y coordinates of ShowsScrollView frame, to move it from bottom to top right corner. Then I resize it (change width and height of ShowsScrollView frame) and reorder items in this scroll. At the end I setup new contentSize.
Now i get touches event only on first 1/4 of scrollview, scrolling also work only on 1/4 of scrollview, but scroll all items in scrollview.
After all actions I write a log: NSLog(#"ViewController: setLandscape finished: size: %f, %f content: %f,%f",scrollView.frame.size.width,scrollView.frame.size.height, scrollView.contentSize.width, scrollView.contentSize.height );
Values are correct:
ViewController: setLandscape finished: size: 390.000000, 723.000000 content: 390.000000,950.000000
On rotating back to portrait, I move and resize all thing back and everything works fine..
Please help!
I had the same issue and managed to get it working by setting the size of self.view again AFTER changing the scrollview's frame. I don't know why this works, but it does.
// Set the view's frame
if (UIDeviceOrientationIsLandscape(toOrientation)) {
[self.view setSize:CGSizeMake(1024, 724)];
} else {
[self.view setSize:CGSizeMake(768, 960)];
}
// Set scrollview frame
if (UIInterfaceOrientationIsLandscape(toOrientation)) {
self.scrollView.frame = CGRectMake(0, 0, 100, 300);
self.scrollView.contentSize = CGSizeMake(100, 600);
}
else {
self.scrollView.frame = CGRectMake(0, 0, 300, 100);
self.scrollView.contentSize = CGSizeMake(600, 100);
}
// Move your content around here
// Set the view's frame again
if (UIDeviceOrientationIsLandscape(toOrientation)) {
[self.view setSize:CGSizeMake(1024, 724)];
} else {
[self.view setSize:CGSizeMake(768, 960)];
}
You have to bring scrollView to front of the superView, for example:
[self.view bringSubviewToFron:scrollView];