UIScrollview frame only updating correctly after scrolling - cocoa-touch

So I have created a UIScrollView as a subview of the view controller's view in Interface builder and set all auto layout constraints so that it resizes to fill its superview (Trailing, leading, bottom and top constrains set to zero with regards to its superview).
When I run the application, the scrollview does not initially update its frame to be the same size as its superview. the scrollview frame.size.width = 320 even though I am running it on iPhone 6 or iPhone 6 plus simulator.
As soon as I start scrolling, the scrollview frame gets updated to the correct value (same as its superview). why is this happening? shouldn't auto layout automatically resize the scrollview in relation to its superview?
The only way I managed to fix this is to add the following in viewDidLoad:
scrollView.frame = view.frame.
Any ideas why this is happening? It does not seem logical.
Many thanks

Ok, so after some experimentation, I figured it out.
I was checking the scrollview frame size in viewDidLoad and it was returning 320
However, it seems that auto layout will only do resizing AFTER viewDidLoad and will call viewDidLayoutSubviews() after it has finished resizing/repositioning...etc
Therefore, when I check the frame size inside viewDidLayoutSubviews, I get the right frame size 375 (for iPhone 6).
Thank you

Related

UIScrollView doesn't work well with IB

I have a screen full of different UI objects that I need to scroll if on a 3.5 inch iPhone screen. I've added the ScrollView and setEnabled to YES but it still doesn't work. I'm thinking I should just code all the objects instead of using the IB and then add them to the scrollview. If I use a view with a scrollview on top with a view on top of that, then add them all to the subview, they lose their positioning. As for the constraints on the IB, there are simply too many objects and with hours spent on different constraint use, that will not be an option. Thanks for any suggestions and help.
Try to set the content size to be the size your frame, which is this case your view controller since you mentioned its "a screen full of different UI objects"
self.scrollView.contentSize = self.scrollView.frame.size;
then set scrollView frame to be the size of the view's frame
self.scrollView.frame = self.view.frame;
UIScrollView wont scroll, if the contents inside of the UIScrollview, are small enough to view without scrolling.
So if you have a UIView obj inside of your UIScrollView and that UIView is not Bigger than the UIScrollView, by default the UIScrollview will not scroll.

Achieve effect where UITableView moves before scrolling

Description
If you use the Spotify app on iOS or Android (I am developing on iOS), you'll notice that if you select a playlist, and drag the UITableView with your songs up, the following happens:
The table doesn't scroll, it just moves up at the same speed it would scroll, and the images above it move slower than the table, creating a parallax scrolling effect.
Once the tableview reaches the top of the view, however, it behaves like a normal scrolling tableview.
I've tried to achieve this effect a few different ways, which didn't work for me.
Here is a video of this effect: https://www.dropbox.com/s/n7npk4lrzmag0sn/IMG_9331.MOV
What I tried
I wanted the UITableView and the UIScrollView above it, to move upwards when the user scrolled either one, so I used
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
and based on the direction of the scroll, I changed the frame position of the tableview and the UIScrollView above it.
The problem with this method is that the tableview bounces, which ruins the effect.
To get rid of bouncing, I tried the following:
_userTableView.bounces = NO;
However, now because the tableview wouldn't scroll, scrollViewDidScroll is never called.
The other thing I tried was subclassing the UITableView, and overriding the
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
hitTest method to detect a scroll gesture on the tableview, the problems with this are:
Only 1 CGPoint is passed -- it's impossible to know if the UITableView was touched, or scrolled.
Since scrolling is disabled, I can't use self.decelerating to check if the tableview was just scrolled -- the tableview will never be decelerating
Making the table decelerate as it moves up the view, and continue scrolling with the same momentum after its reached the top will be very challenging; In the Spotify app, a user can drag the table with a speed high enough to make the table view move to the top of the view AND continue scrolling, all in one motion.
The tableview should grow in size to accommodate more cells added to the view (without scrolling), as it moves up the view, or it should start out with the same size as the view. After it reaches the top it should begin scrolling like a normal tableview -- with the same momentum it has after the upward movement.
Any suggestions on how I can solve this?
Thank you very much.
For the effect you have shown, I think a tableHeaderView with the parallax content as a subview will suffice.
In -scrollViewDidScroll: (or KVO for contentOffset), you set the center of the parallax subview to the center of the visible bounds of the tableHeaderView. You can also adjust how much the content "sticks" to the center by multiplying a factor.
CGRect tableViewHeaderVisibleBounds = tableViewHeader.bounds;
tableViewHeaderVisibleBounds.origin.y = MAX(0, MIN(CGRectGetHeight(tableViewHeader.bounds), tableView.contentOffset.y));
tableViewHeaderVisibleBounds.size.height -= tableViewHeaderVisibleBounds.origin.y;
CGFloat factor = 1.8f; // less than 0.5:stick to top // greater than 0.5:stick to bottom
parallaxContentView.center = (CGPoint){
.x = parallaxContentView.center.x,
.y = (CGRectGetMinY(tableViewHeaderVisibleBounds)
+ (CGRectGetHeight(tableViewHeaderVisibleBounds) * factor))
};
I coded this inside my head so just make necessary adjustments, but I hope you get the basic idea.

How do I resize a UIView and force its subviews to resize

I have a UIScrollView with a UIView as a subview. The UIView has a bunch of data entry fields arranged vertically - essentially just a fixed format data entry Form.
I want to keep the UIView's vertical size and adjust its horizontal size to match the size of the UIScrollView which changes depending on the orientation of the device. Note that this is all placed in the Detail view of a UISplitViewController.
So the user will have to scroll vertically but not horizontally as all the text fields on the UIView should resize themselves to fit horizontally on the screen.
Currently if I resize the UIView by changing the frame width to match the UIScrollView's frame width then the UIView subviews (the text fields) don't resize themselves according to the constraints setup in IB. The UIView just seems to get clipped. There is no horizontal scrolling so this aspect is working correctly.
I have autoresize subviews set on UIView and on UIScrollView.
Any tips on what to do here ? Also where would I put code to resize the UIView if the device orientation changes ?
Additional information.
I created the UIView in IB as a separate view in the same NIB as the DetailViewController containing the UIScrollView. Because it is much taller than the UIScrollView the only way I can find for creating it in IB is if I set it up as a separate view of the desired width and height. I then create an IBOutlet and add this view as a subview to UIScrollView in the viewDidLoad method. This all seems to work find with the views all displaying correctly, with the exception that the UIView subviews are not resized horizontally.
Any suggestions on what I may be doing wrong here?
Since you are using not putting the view inside scrollview directly from the xib, the IB doesn't provide the options to give constraints that has anything to do with superview. You might have to add the constraints programatically.See here.
EDIT:
Also try using the below on the view (haven't tried, should work according to documentation, but not sure with auto-layout):
self.view.autoresizesSubviews = YES;
Or if you don't want to use auto layout at all then the earlier method of setting the view to expand (horizontal/vertical) in the size inspector would do. For this you have to disable auto-layout. Select xib-> File Inspector -> Uncheck auto-layout checkbox
OK after more discovery I think I have found the right way to do what I am trying to do so i thought I should leave a note regarding this. Remember I am trying to create a scrollable form that resizes its width but not its height so the user only has to scroll up and down to access fields.
To create a large fixed size form that requires scrolling on the device make sure you set the ViewController size to Freeform in IB. Then you can create the view to be whatever size you want in IB and at runtime it will resize to the devices size.
Place the UIScrollView (I call it the scrollView) in the main view and pin it left and right and top and bottom (i.e set constraints using IB)
Place a UIView (I call it the contentView) in scrollView and make it the same size as the scrollView and also pin it on all sides to its superview (the scrollView)
Now add all the labels and text fields are required to the contentView and make sure you add vertical constraints from top to bottom and left to right so that autolayout can figure out the width and height in order to calculate the scrollView.contentSize
Set the contentView width constraint as a fixed size to make it look reasonable in IB. Bear in mind we want the width of contentView to always match the scrollView width so the user does not have to scroll sideways, only up and down. We will set the proper width in code at runtime as I have not found any way of doing this in IB only. Perhaps setting priorities on constraints might achieve this but I think UIScrollView won't do this for you.
Now add a property the ViewController.h file and connect this to the contentView width constrain you created in 5) above.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewWidth;
Finally create a viewDidLayoutSubviews method in ViewController and add the following code to set the contentView width to be the same as the scrollView width.
- (void)viewDidLayoutSubviews
{
self.contentViewWidth.constant = self.scrollView.frame.size.width;
[self.view layoutSubviews];
}
If things don't resize properly check all your constraints are correctly set. IB seems to do some things that seem strange to me. But finally I have it working with what appears to be minimum coding.
You can also resize vertically in the same way as long as you set the constraint priority on subviews in contentView to be lower than 1,000. Also set a greater than or equal to size with a high priority if you don't want it smaller than a certain size.
If anyone can figure out how to set the contentView such that it resizes its width to match the scrollView using only IB constraints I would love to know how.

UITableView scroll speed after frame resize

I have a UITableView which covers half of the screen in iPad portrait mode. Whenever I click, expand button which calculates and animates the tableview's superview's frame to cover the whole screen, scrolling becomes very messy and flickering even with a single row. Especially bounce animation seems to be longer. TableView is masked with autoresizeheight. After I restore superview's frame, tableview scrolling becomes smoother again. Are there any relation between tableview's initial frame and the scrolling speed? What are the possible root causes for this kind of behavior?
Maybe you inserted the resize inside your cellForRowAtIndexPath method, so it sets the size tableview every time you scroll. I inserted this code snippet inside a check in viewDidLoad, then the size will be set just once at the beginning and everything works.
CGPoint tvFrameO = _TableView.frame.origin;
CGSize tvFrameS = _TableView.frame.size;
float valueToResize = 50.0f;
_TableView.frame = CGRectMake(tvFrameO.x,
tvFrameO.y,
tvFrameS.width,
tvFrameS.height + valueToResize);

How do I resize a UIScrollView so that it's not 100% of the parent UIView?

I've built an app using the UITabBar template. I have a few tabbar items, one item displays a view. That view has a UIScrollView element that has paging enabled to mimic the behaviour of the iPhone springboard i.e. pages that can be scrolled left to right.
I'm trying to drop in a UIPageControl, so I've resize the UIScrollView so that it's slightly shorter than the parent UIView height and have placed a UIPageControl below it.
When I run the app the UIScrollView is always 100% of the height of the parent UIView and I can't see the UIPageControl.
I've got the following code in my viewDidLoad method of the view controller for the tab:
UIScrollView *tempScrollView=(UIScrollView *)self.view;
tempScrollView.contentSize=CGSizeMake(640,377);
This sets the content size ok and I can scroll left to right. I've tried adding:
tempScrollView.frame=CGRectMake(0, 0, 640, 377);
To to resize the scroll view but it still shows 100%. See diagram below showing the issue:
I think you shouldn't resize the frame to 640, 377 because it would make the paging stop working, once the contentSize would be the same as the frame size.
One solution would be to set the desired frame size in the interface builder (like the left most figure) and set proper autosizing masks. I gues what you are looking for is the configuration below
To check if the changes are working, I would use a uiscrollview of height visibly smaller, just to make sure the behaviour is the desired.
If you want your view to scroll you need to change the tempScrollView.contentSize to be bigger than tempScrollView.frame
If you do this:
CGFloat contentWidth = tempScrollView.frame.size.width*2;
tempScrollView.contentSize=CGSizeMake(contentWidth,377);
You will have 2 pages.
You need to activate paging too with:
[tempScrollView setPagingEnabled:TRUE];