当子视图超出可见区域时,自动滚动UIScrollView

时间:2012-09-10 13:31:02

标签: objective-c ios uipangesturerecognizer

修改

我将我的实施上传到github。也许最好先了解我想要什么以及我的问题是什么。 我在github项目中的代码比这里发布的代码稍微改了一些。我认为github项目中的实现更好,但并不完美。

我想做什么:

拥有带可移动UIViews的UIScrollView(例如图像)。用户可以平移此子视图并放大和缩小。当用户放大并在当前可见区域上移动子视图时,scrollView应自动滚动。由于子视图位于边缘,因此scrollview应滚动。当子视图不再在可见区域上时,滚动视图应该停止移动。

我试着尽可能好地解释我的问题。

我设法做了什么:

缩放滚动视图,使用UIPanGestureRecognizer移动子视图,识别子视图何时在可见区域上并开始移动(更改contentOffset)scrollview。只要子视图在可见区域上,我就可以使用NSTimer移动滚动视图。

我的问题:

当子视图在可见区域上时,启动NSTimer,以更改子视图的contentOffset和子视图的帧(位置)。 在那之后我再也无法平移子视图了。 我无法弄清楚如何以正确的方式更改子视图帧来实现平移手势。

我的实施:

我使用三种观点:

  • UIScrollView
  • MyImageContainerView(UIView,作为滚动视图的子视图添加)
  • MyImageView(UIView,作为MyImageContainerView的子视图添加)

目前,MyImageContainerView管理工作流程。 MyImageView附加了UIPanGestureRecognizer。此识别器的方法在MyImageContainerView中实现:

- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
    //UIView which is moved by the user
    MyImageView *currentView = gestureRecognizer.view;

    switch (gestureRecognizer.state)
    {
        case UIGestureRecognizerStatePossible:
        {
            break;
        }
        case UIGestureRecognizerStateBegan: 
        {
            //save both values in global instance variables
            currentFrameOriginX = currentView.frame.origin.x;
            currentFrameOriginY = currentView.frame.origin.y;

            //global BOOL variable, to check if scrollView movement is performed
            scrolling = NO;        
            break;
        }

        case UIGestureRecognizerStateChanged:
        {            
            CGRect rect = CGRectMake(currentFrameOriginX + [gestureRecognizer translationInView:currentView.superview].x, currentFrameOriginY + [gestureRecognizer translationInView:currentView.superview].y, currentView.frame.size.width, currentView.frame.size.height);

            if (CGRectContainsRect(currentView.superview.frame, rect)) {

                /*PROBLEM: Here is a problem. I need this change of the frame here, to move the UIView along the movement from the user. In my autoScroll-method I have to set the frame of currentView, too. But I can't set the frame of currentView here and in the autoScroll. But as long as the NSTimer runs and is calling autoScroll: this if-statement isn't called, so I can't move the UIView with my finger anymore. */
                if (!scrolling) {
                    //currently the NSTimer method for the automatically scrolling isn't performed, so:
                    //change the frame according to the pan gesture
                    currentView.frame = rect;
                }

                UIScrollView *scrollView = self.myScrollView; //reference to the "root" UIScrollView
                CGRect visibleRect;
                visibleRect.origin = scrollView.contentOffset;
                visibleRect.size = scrollView.bounds.size;

                CGRect frame = currentView.frame;

                CGFloat scale = 1.0 / scrollView.zoomScale;
                visibleRect.origin.x *= scale;
                visibleRect.origin.y *= scale;
                visibleRect.size.width *= scale;
                visibleRect.size.height *= scale;

                CGSize scrollZone = CGSizeMake(10.0f, 10.0f);
                float scrollStep = 3.0f;
                CGPoint scrollAmount = CGPointZero;

                //determine the change of x and y
                if (frame.origin.x+scrollZone.width < visibleRect.origin.x) {
                    scrollAmount.x = -scrollStep;
                }
                else if((frame.origin.x+frame.size.width)-scrollZone.width > visibleRect.origin.x + visibleRect.size.width) {
                    scrollAmount.x = scrollStep;
                }
                else if (frame.origin.y+scrollZone.height < visibleRect.origin.y) {
                    scrollAmount.y = -scrollStep;
                }
                else if((frame.origin.y+frame.size.height)-scrollZone.height > visibleRect.origin.y + visibleRect.size.height) {
                    scrollAmount.y = scrollStep;
                }

                if ((scrollAmount.x != 0) | (scrollAmount.y != 0)) {
                    if (![scrollTimer isValid]) {
                        //scrollTimer is a global NSTimer instance variable
                        [scrollTimer invalidate];
                        scrollTimer = nil;

                        NSString *scrollString = NSStringFromCGPoint(scrollAmount);
                        NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:scrollString, @"scrollString", currentView, @"currentView", nil];

                        scrollTimer = [[NSTimer alloc]initWithFireDate:[NSDate date] interval:0.03f target:self selector:@selector(autoScroll:) userInfo:info repeats:YES];
                        [[NSRunLoop mainRunLoop] addTimer:scrollTimer forMode:NSRunLoopCommonModes];
                    }
                }
                else {
                    [scrollTimer invalidate];
                    scrollTimer = nil;
                    scrolling = NO;
                }
            }
            break;
        }
        case UIGestureRecognizerStateEnded:
        {
            //quite know the scrolling should stop, maybe it would be better when the scrollView scrolls even if the user does nothing when the subview is over the visible area
            [scrollTimer invalidate];
            scrollTimer = nil;
            scrolling = NO;
            break;
        }
        default:
        {
            [scrollTimer invalidate];
            scrollTimer = nil;
            scrolling = NO;
            break;
        }
    }
}


-(void)autoScroll:(NSTimer*)timer {

    scrolling = YES; //the scroll method is executed quite know

    NSDictionary *info = [timer userInfo];

    UIScrollView *scrollView = self.myScrollView;
    CGRect visibleRect;
    visibleRect.origin = scrollView.contentOffset;
    visibleRect.size = scrollView.bounds.size;

    CGPoint scrollAmount = CGPointFromString([info objectForKey:@"scrollString"]);
    MyImageView *currentView = [info objectForKey:@"currentView"];

//stop scrolling when the UIView is at the edge of the containerView (referenced over 'self')
    if ((currentView.frame.origin.x <= 0 | currentView.frame.origin.y <= 0) ||
        ((currentView.frame.origin.x+currentView.frame.size.width) > self.frame.size.width | (currentView.frame.origin.y+currentView.frame.size.height) > self.frame.size.height)
        ) {
        scrolling = NO;
        return;
    }

    //move the UIView 
    CGFloat scale = 1.0 / scrollView.zoomScale;
    if (scrollAmount.x != 0) {
        scrollAmount.x *= scale;
    }
    if (scrollAmount.y != 0) {
        scrollAmount.y *= scale;
    }
    CGRect frame = currentView.frame;
    frame.origin.x += scrollAmount.x; 
    frame.origin.y += scrollAmount.y;
    currentView.frame = frame;
    currentFrameOriginX = currentView.frame.origin.x;
    currentFrameOriginY = currentView.frame.origin.y;

        //move the scrollView
    CGPoint contentOffset = scrollView.contentOffset;
    contentOffset.x += scrollAmount.x;
    contentOffset.y += scrollAmount.y;
    [scrollView setContentOffset:contentOffset animated:NO];
}

0 个答案:

没有答案