UIScrollView view port coordinates - ios

This is how I am adding UIScrollView in my viewDidLoad
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
scrollView.backgroundColor = [UIColor redColor];
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = YES;
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.contentSize = CGSizeMake(self.view.bounds.size.width , self.view.bounds.size.height * 2);
[self.view addSubview:scrollView];
scrollView.delegate = self;
When the scrollView is loaded for the first time it's coordinates are : {(0,0), (widthOfiPhone, 0), (0, heightOfiPhone), (widthOfiPhone, heightOfiPhone)}
Whenever the scroll finishes, I want to get the co-ordinates of viewport in scrollview. How to do this? Thanks
Edit:
Image Credits: https://oleb.net/blog/2014/04/understanding-uiscrollview/

First, observe scrollview agent, and then implement its proxy method
(void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView {
NSLog (# "% f,% f,% f,% f", scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height);
}
This should be possible

You can implement UIScrollView delegate method to detect when scroll is end : scrollViewDidEndDecelerating and in this method you can get coordinates of your scrollview and it's content. Btw content's cooridinate will be same with respect scrollview!!!
You can get visible rect from scrollview like,
CGRect visibleRect = [subViewOfScrollView convertRect:subViewOfScrollView.bounds toView:self.view];

Related

Adding scrollview to a view

I have tried Adding UIScrollView to a UIViewController, and I can never get my view to move down when I scroll, either on a simulator on a real device. I have also tried increasing the size, so my code is as follows:
+(void)addScrollViewToView:(UIView *)view
{
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, view.bounds.size.width * 2, view.bounds.size.height * 2)];
scrollView.backgroundColor = [UIColor clearColor];
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = YES;
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.contentSize = CGSizeMake(view.bounds.size.width * 2, view.bounds.size.height * 2);
[view insertSubview:scrollView atIndex:0];
//[view sendSubviewToBack:scrollView]; - commented out
}
It is called within viewDidLoad like so:
#implementation MyLoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[Utils addScrollViewToView:self.view];
self.passwordTextField.secureTextEntry = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
} // viewDidLoad method ends here
There is never any response when I try and scroll up with my mouse on a simulator. There is a background image to my view controller that I created using a storyboard. Would changing the index make any difference?
It happen because you add gesture recognizer. Gesture recognizer blocks and not pass your touch.
You can make this for dismiss keyboard with UIScrollView:
+(void)addScrollViewToView:(UIView *)view
{
// your code
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
}
And remove UITapGestureRecognizer.
Or is it because your frame size is the same as your content size? The scroll view will only scroll if its content is bigger than its frame.
What did the trick was adding the view to the scrollview.
I.e.:
View >
ScrollView >
View that I wanted to scroll
Not:
View that I wanted to scroll >
ScrollView.

UIScrollView paging enabled with content inset is working strange

I created the UIScrollView with content insets.
scrollView.frame = CGRectMake(-80, 180, 480, 190)
self.scrollView.contentInset = UIEdgeInsetsMake(0, 160, 0, 160);
self.scrollView.pagingEnabled = YES;
[self.scrollView setContentSize:CGSizeMake(480, 190)];
// Add three Views
[self.scrollView addSubview:view1];
[self.scrollView addSubview:view2];
[self.scrollView addSubview:view3];
[view1 setFrame:CGRectMake(0, 0, 160, 190)];
[view2 setFrame:CGRectMake(160, 0, 160, 190)];
[view3 setFrame:CGRectMake(320, 0, 160, 190)];
At first time, scrollView.contentOffset.x was -160.0
But the weird problem is when I tap on scrollView(Yellow Area), content offset x value is resetting to 0 and shown like this.
I tried several times, but tapping on Scroll View resets the content offset to 0.
How can I prevent this?
UIScrollView paging works by scrolling pages with the same width of the scrollView (in your case pages of 480 width). This means that you have 1 single page (you'd still be able to scroll left and right due to the 160 content inset).
One way to make this work would be:
self.scrollView.frame = CGRectMake(80, 180, 160, 190);
self.scrollView.clipsToBounds = NO;
self.scrollView.contentInset = UIEdgeInsetsZero;
self.scrollView.pagingEnabled = YES;
[self.scrollView setContentSize:CGSizeMake(480, 190)];
This will draw and scroll correctly, however, the sides of the screen will not be interactive (80 pixels on each side, since the control starts at frame.origin.x=80 and ends at 80+160=240).
Second option would be to handle paging yourself, by using methods provided by UIScrollViewDelegate.
- (void)viewDidLoad
{
[super viewDidLoad];
// pageIndex must be declared as a class member - this is used to prevent skipping pages during scroll
pageIndex = 0;
self.scrollView.frame = CGRectMake(0, 180, 320, 190);
self.scrollView.contentInset = UIEdgeInsetsMake(0, 80, 0, 80);
self.scrollView.pagingEnabled = NO;
self.scrollView.clipsToBounds = YES;
[self.scrollView setContentSize:CGSizeMake(480, 190)];
self.scrollView.delegate = self;
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
int pageWidth = 160;
int pageX = pageIndex*pageWidth-scrollView.contentInset.left;
if (targetContentOffset->x<pageX) {
if (pageIndex>0) {
pageIndex--;
}
}
else if(targetContentOffset->x>pageX){
if (pageIndex<3) {
pageIndex++;
}
}
targetContentOffset->x = pageIndex*pageWidth-scrollView.contentInset.left;
NSLog(#"%d %d", pageIndex, (int)targetContentOffset->x);
}
In addition to alex's first approach with setting clipsToBounds to NO, I need to react to the scroll outside of the scroll view bounds. So I created ScrollForwarderView class which is subclass of UIView.
ScrollForwarderView.h
#import <UIKit/UIKit.h>
#interface ScrollForwarderView : UIView
#property (nonatomic, weak) UIScrollView *scrollView;
#end
ScrollForwarderView.m
...
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
return _scrollView;
}
return nil;
}
Then place UIView with custom class ScrollForwarderView above the scroll view, link scrollView property to my scroll view and it nicely forwarded the user events to scrollview.

UIScrollView can zoom, but cant pan

I have a UIScrollView with a UIView inside it, in side the UIView I made a button to add textLabels to it.
and ideally I would want a really big canvas and be able to put text on it and pan and zoom around. however with the UIScrollView it does zoom, but does not pan at all
It seems that when I remove the UIView that i add inside the UIScrollView it works fine.
heres viewDidLoad:
[super viewDidLoad];
CGFloat mainViewWidth = 700;
CGFloat mainViewHeight = 500;
//scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height * kNumberOfPages);
//self.mainScrollView.bounds = CGRectMake(0., 0., 3000, 3000);
self.mainScrollView.scrollsToTop = NO;
self.mainScrollView.delegate = self;
self.mainScrollView.maximumZoomScale = 50.;
self.mainScrollView.minimumZoomScale = .1;
self.mainScrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.mainView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, mainViewWidth, mainViewHeight)];
[self.mainView setUserInteractionEnabled:NO];
self.mainView.backgroundColor = [[UIColor alloc] initWithRed:0.82110049709463495
green:1
blue:0.95704295882687884
alpha:1];
[self.mainScrollView setContentSize:CGSizeMake(5000, 5000)];
[self.mainScrollView insertSubview:self.mainView atIndex:0];
Edit:
Heres the all I have for UIScrollViewDelegate
#pragma mark - Scroll View Delegate
- (void)scrollViewDidScroll:(UIScrollView *)sender {
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
//
return self.mainView;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)zoomedScrollView withView:(UIView *)view atScale:(float)scale
{
}
I just went through this exact same dilemma.
My UIScrollView exists within the storyboard, and if I add a UIView (containerView) within that storyboard to the UIScrollView, the image fails to pan. And all sorts of other centering weirdness occurs too.
But if I do it through code:
// Set up the image we want to scroll & zoom
UIImage *image = [UIImage imageNamed:#"plan-150ppi.jpg"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.containerView = [[UIView alloc] initWithFrame:self.imageView.frame];
[self.containerView addSubview:self.imageView];
[self.scrollView addSubview:self.containerView];
// Tell the scroll view the size of the contents
self.scrollView.contentSize = self.containerView.frame.size;
... then it works just fine.

How-To forward pan gesture from a scrollview to another scrollview

I have two UIScrollViews and i want to let the other scrollview scroll too if the user scrolls in a scrollview. I have read a solution that involves passing the pangesturerecognizer from the originally panned scrollview to the other scrollview but if i do that the original scrollview does not scroll at all.
I found out, that there is a delegate method
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
but if I try to hook up the delegate of the pangesturerecognizers of the scrollviews my app crashes.
Hope someone can help me.
You Just need to update the Bounds of the second ScrollView
Something like :-
CGRect updateTheViewWithBounds = viewToUpdate.bounds;
updateTheViewWithBounds.origin = scrolledView.contentOffset;
viewToUpdate.bounds = updateTheViewWithBounds;
This Will do the Job.
As Seen from comments , I will give a small Example.
Create the two scrollView onto The UiViewController
scrollOne = [[UIScrollView alloc] init];
[scrollOne setBackgroundColor:[UIColor greenColor]];
[scrollOne setFrame:CGRectMake(10, 20, 200, 300)];
[scrollOne setContentSize:CGSizeMake(600, 600)];
scrollOne.delegate = self;
scrollTwo = [[UIScrollView alloc] init];
[scrollTwo setBackgroundColor:[UIColor redColor]];
[scrollTwo setFrame:CGRectMake(230, 20, 200, 300)];
[scrollTwo setContentSize:CGSizeMake(600, 600)];
scrollTwo.delegate = self;
[self.view addSubview:scrollOne];
[self.view addSubview:scrollTwo];
Conform to the UIScrollView Delegates and implement the same.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if([scrollView isEqual:scrollOne])
{
CGRect updateTheViewWithBounds = scrollOne.bounds;
updateTheViewWithBounds.origin = scrollOne.contentOffset;
scrollTwo.bounds = updateTheViewWithBounds;
[scrollTwo flashScrollIndicators];
}
else if([scrollView isEqual:scrollTwo])
{
CGRect updateTheViewWithBounds = scrollTwo.bounds;
updateTheViewWithBounds.origin = scrollTwo.contentOffset;
scrollOne.bounds = updateTheViewWithBounds;
[scrollOne flashScrollIndicators];
}
}
Scrolling any of the above scrollView will scroll both the scrollView.

UIScrollviews zoomToRect only sets the zoomScale but not the contentOffset

My UIScrollView is not setting its contentOffset when using zoomToRect.
I have an UIScrollView with an UIImageView inside. Scrolling and zooming itself is working so far. Now I want to give the scrollview at app start a certain zoomed rect of the image view. For this I implemented zoomToRect: and it is setting the zoomsScale correctly, but it does not set the contentOffset.
Expected outcome when using zoomToRect is that the UIScrollView zooms in or out according to the selected rect and set its contentOffset according to the origin coordinates of the rect given to the zoomToRect method.
The actual behaviour is that it zooms to the correct zoomScale but my UIImageView is always at the origin 0,0 and not the expected origin of the x (475) and y (520) coordinated of the rect I specified in zoomToRect.
My images size is 1473x1473.
Here is some the code
- (void)viewDidLoad {
CGRect bounds = self.view.frame;
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bgImage.png"]];
self.containerView = [[UIView alloc] initWithFrame:bounds];
_containerView.contentMode = UIViewContentModeCenter;
_scrollView = [[UIScrollView alloc] initWithFrame:bounds];
_scrollView.delegate = self;
_scrollView.contentSize = _imageView.bounds.size;
_scrollView.minimumZoomScale = 0.2;
[_scrollView addSubview:_containerView];
[_containerView addSubview:_imageView];
[self.view addSubview:_scrollView];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[_scrollView zoomToRect:CGRectMake(475.0, 150.0, 520.0, 747.0) animated:NO];
}
#pragma mark UIScrollViewDelegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _containerView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self printVisibleRectToConsole:scrollView];
CGSize newImageViewSizeWithScale = CGSizeMake(_imageView.bounds.size.width * _scrollView.zoomScale,
_imageView.bounds.size.height * _scrollView.zoomScale);
_scrollView.contentSize = newImageViewSizeWithScale;
}
My questions:
Why does zoomToRect does not set the contentOffset?
How can I get zoomToRect to change my contentOffset as expected?
The problem is that the view you're zooming (containerView) is not as big as the image view it contains (and which you actually want to zoom). Its frame is set to the frame of the view controller. You don't see this because a UIView doesn't clip its subviews by default.
You should initialize containerView with the bounds of your image view instead.
self.containerView = [[UIView alloc] initWithFrame:_imageView.bounds];

Resources