2

編集:

実装をgithubにアップロードしました。たぶん、私が何を望んでいて、私の問題が何であるかを理解する方が良いでしょう. ここに投稿されたコードよりも少しgithubプロジェクトのコードを変更しました。github プロジェクトでの実装の方が優れていると思いますが、完全ではありません。

私がしたいこと:

移動可能な UIView (画像など) を持つ UIScrollView を用意します。ユーザーは、このサブビューをパンしてズームインおよびズームアウトできます。ユーザーがサブビューをズームインして現在の表示領域に移動すると、scrollView は自動的にスクロールする必要があります。サブビューが端にあるのと同じように、スクロールビューはスクロールする必要があります。サブビューが表示領域から外れると、スクロールビューの移動が停止します。

私は自分の問題をできるだけうまく説明しようとします。

私がなんとかしたこと:

スクロールビューをズームし、UIPanGestureRecognizer を使用してサブビューを移動し、サブビューが表示領域上にあることを認識して、スクロールビューの移動 (contentOffset の変更) を開始します。ここでは、サブビューが表示領域上にある限り、NSTimer を使用してスクロールビューを移動します。

私の問題:

サブビューが可視領域を超えると、NSTimer が開始され、サブビューの contentOffset とサブビューのフレーム (位置) が変更されます。その後、サブビューをパンできなくなりました。サブビュー フレームを正しい方法で変更してパン ジェスチャを実装する方法がわかりません。

私の実装:

私は3つのビューを使用しています:

  • 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