I have implemented an UIScrollView delegate as:
- (void) scrollViewDidScroll: (UIScrollView *) scrollView {
CGRect bounds = scrollView.bounds ;
CGPoint scrollLoc = scrollView.contentOffset ;
NSLog(#"bounds: %# offset:%#"
, NSStringFromCGRect(bounds)
, NSStringFromCGPoint(scrollLoc)) ;
}
And whatever I do, scrolling or rotating the device, it seems that contentOffset and bounds.origin are always the same.
Why do we need a contentOffset if that is the same as the bounds origin, or what is the case when both are actually different?
According to the docs:
The contentOffset property is always the current location of the
top-left corner of the scroll bounds, whether scrolling is in progress
or not.
The View is at the same location in both case, but the content may be moved within the view if you use a ContentOffset.
You will find usefull information in episode 9.Image View ... of the Standford University series :
http://itunes.apple.com/fr/itunes-u/developing-apps-for-ios-hd/id395605774
As far as I know, they both offer you the same information, one as a rectangle with his width and height and the other just the origin point.
Related
There is a button at the bottom of my view controller. When the user scrolls down the button has to be attached to the scrollview at certain height.
I need to attach a button to the scrollview, immediately when the contentOffset.y reaches a particular value. -(void) scrollviewDidScroll doesn't help me as there might be a jump in contentOffset when the user is scrolling fast. Any leads on this are helpful.
Also, whenever I add a subview to the scrollview, -(void) viewDidLayoutSubviews is called. Which in turn sets the contentOffset to {0,0}. How can I achieve the functionality I need?
I needed to do the same thing with a UITableView and for me using scrollViewDidScroll worked.
I created a view called staticBar and added it as a subview of the tableView, but I had to rearrange the tableview subviews for it to appear in the right place. I don't have my code in front of me, but in -scrollViewDidScroll: it looked something like this:
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
{
CGFloat staticBarAdjustedY = _staticBarY - scrollView.contentOffset.y;
CGFloat scrollViewYFloor = scrollView.frame.size.height - _staticBar.frame.size.height;
// This way maximum Y the view can have is at the base of the scrollView
CGFloat newY = MIN( staticBarAdjustedY, scrollViewYFloor);
_staticBar.frame = (CGRect){ { _staticBar.frame.origin.x, newY}, _staticBar.frame.size}
}
I will check my code later today and add more details here.
Also, you said the scrollviewDidScroll has jumps in contentOffset, but it's worth mentioning that these jumps are the same that the scrollView uses to scroll its own view. So it's not like you are "losing" frames on this delegate method.
Hope it helps.
PS: So, here is the rest of my code.
//I place my custom view as a subview of the tableView below it's last subview
//The last subview is for scroll indicators.
WTButtonsBar *buttonBar = [[WTButtonsBar alloc] init];
[self.tableView insertSubview:buttonBar belowSubview:self.tableView.subviews.lastObject];
In scrollViewDidScroll:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//In my app I needed my view to stick to the top of the screen
//thats why I use MAX here
//self.buttonsBarOriginalY is the view's position in the scrollView when it isn't attached to the top.
CGFloat newY = MAX(scrollView.contentOffset.y, self.buttonsBarOriginalY)
[_buttonsBar setFrame:(CGRect){{0, newY}, _buttonsBar.frame.size}];
}
I have one UIImageView (building's floor map) and UIScrollView with gesture recognisers which allow me to scroll horizontally and vertically, zoom in/out. Everything works OK, but the building has two floors, so user should have the option to switch between them. I decided to use segmented control at the top of the map to provide this option.
If I put Segmented Control to the same UIScrollView, it scrolls vertically as well as horizontally. What I am asking is how to fix horizontal position of the Segmented Control, so it will be able to scroll only vertically with map.
I am trying to use this code to test, if it fixes the position of Segmented Control absolutely, but it seems to be that it doesn't.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = _floorSelector.frame;
frame.origin.y=50;
frame.origin.x= 160;
_floorSelector.frame = frame;
}
Where is the mistake? Thank you for replies!
I think the issue here is you are misunderstanding how scrolling is implemented in a UIScrollView, and how things are placed in its coordinate space.
When a UIScrollView is scrolled, the frames of its subviews are not changed. Relative to the scrollview, they remain in the same position while the contentOffset and bounds of the scroll view changes. In order to make a subview of a scroll view appear static while the rest of it scrolls, you must change its frame within the scrollview as the bounds change.
With that in mind, what you are doing now is just setting the same frame on that subview repeatedly, which is the same as not doing anything.
It sounds like what you want to do is something like this:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = _floorSelector.frame;
frame.origin.x = 160 + scrollView.contentOffset.x;
_floorSelector.frame = frame;
}
Notice I do not change anything in the y axis because based on your question, the vertical scrolling doesn't need to be changed.
I have a UIScrollView that scrolls horizontally. I have about 10 views lined up horizontally in that scrollview. I want to find the view that contains the center point of the scrollview's frame (not contentView.frame) as it scrolls. I am thinking to use the - (void)scrollViewDidScroll:(UIScrollView *)scrollView UIScrollView delegate method, but I am not sure how to find this.
Thanks!
You can get the start-point of a frame of a view (myChildView) in the reference of another view (scrollView) using the following code:
CGPoint origin = [myChildView convertPoint:CGPointMake(myChildView.bounds.origin.x, myChildView.bounds.origin.y) toView:[self scrollView]];
Use the scrollViewDidScroll: delegate method to pick your views and perform the calculations there.
I am trying to move my UIScrollView to make way for the keyboard when the keyboard is shown.
What's weird is the scrollview is moving too much. I noticed that if I even just set the content offset of the scrollview to (0,0) it still moves the view down. What's bizarre, is the contentOffset of the scrollview is 0,0 before I set it! Whattt!
- (void)keyboardWasShown:(NSNotification*)notification {
NSLog(#"%f %f", scrollView.contentOffset.x, scrollView.contentOffset.x);
[scrollView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
}
This prints (0,0) but if I comment the second line, the view doesn't move. Does setContentOffset have weird secondary consequences?
Try this:
- (void)keyboardWasShown:(NSNotification*)notification {
CGFloat ay = 150;
[scrollView setContentOffset:CGPointMake(0.0, textField.frame.origin.y-ay) animated:YES];
}
Here change the value of ay as per your requirement. Depends on how much you want the scrollview to move.
my question is:
In my app I want realize somethings like this using UIScrollView
A UIScrollView whit all his contents inside. But when I reached the top of the screen (or maybe the bottom of the screen) I want to anchor some contents like in the AppStore (iOS 6).
In this case the "Detail", "Review" and "Related" buttons stop scrolling and remains fixed at the top.
Anyone have any idea how to do?
Give your scroll view a delegate if it doesn't already have one. This would probably be your view controller.
Also give the delegate a reference to the floating view. (In the example, the floating view contains the “Dettagli”, “Recensioni”, and “Correlati” buttons, and the floating view is transparent where the triangular notch is, and the shadow is also part of the floating view.) The floating view must be a subview of the scroll view.
Save the “normal” Y coordinate of the floating view's frame in an instance variable. For example, if the delegate is a view controller, you should probably do it in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
originalFloatingViewY = floatingView.frame.origin.y;
}
In the delegate's scrollViewDidScroll: method, check whether the scroll view has been scrolled below the top of the “normal” position of the floating view. If so, move the floating view to the top edge of the visible area:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self updateFloatingViewFrame];
}
- (void)updateFloatingViewFrame {
CGPoint contentOffset = scrollView.contentOffset;
// The floating view should be at its original position or at top of
// the visible area, whichever is lower.
CGFloat y = MAX(contentOffset.y, originalFloatingViewY);
CGRect frame = floatingView.frame;
if (y != frame.origin.y) {
frame.origin.y = y;
floatingView.frame = frame;
}
}
You could also do it by subclassing UIScrollView and overriding layoutSubviews. Watch the “Advanced ScrollView Techniques” video from WWDC 2011 for more information on this technique.
You need to handle this in the UIScrollView's delegate method( scrollViewDidScroll:) for scrolling. When the y of scrollview reaches that particular value you need to disable scrolling in this direction alone. You just need to add an if condition there to check and reset the y point while scrolling. For this you might have to keep this particular portion as a separate scrollview outside the below content and move it along with the below scroll view's upward movement.