2

编辑:

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

我想做的事:

有一个带有可移动 UIView(例如图像)的 UIScrollView。用户可以平移此子视图并放大和缩小。当用户放大并将子视图移动到当前可见区域时,scrollView 应自动滚动。当子视图在边缘时,滚动视图应该滚动。当子视图不再超出可见区域时,滚动视图应该停止移动。

我尽量解释我的问题。

我设法做到了:

缩放滚动视图,使用 UIPanGestureRecognizer 移动子视图,识别子视图何时超过可见区域并开始移动(更改 contentOffset)滚动视图。在这里,只要子视图在可见区域上方,我就使用 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];
}
4

0 回答 0