7

私は次のレイアウトを持っています(以下を参照)。これはほとんどの状況で問題なく機能します。ユーザーは青色の範囲内でスクロールできUIScrollView(これはすべての目的と目的でですがUITableView、この質問はこれを一般化します)、このスクロールビューの最後に到達すると、再びスクロールを開始できます(内側のスクロールビューがラバーバンドであるため、指を離して再びオンにします)、「スーパー」スクロールビューがスクロールを開始し、画像の残りの部分が表示されます。

私が望まないのは全体です(内側のスクロールビューの輪ゴムのため、指を離してからもう一度オンにする必要があります)。理想的には、含まれているUIScrollViewものがコンテンツの最後に到達したら、スーパービューがすぐにスクロールを引き継いで、内側のスクロールビューがラバーバンドにならないようにします。

ユーザーが上にスクロールしているときも同じことが言えます。赤いスクロールビューがコンテンツの上部に到達したら、上部にある赤いスクロールビューのラバーバンドではなく、内側の青いスクロールビューがすぐに上にスクロールし始めるようにします。

画面レイアウト;  UIScrollView内のUIScrollView+UIImageView

どのようにアイデアはありますか?スクロールビューがいつコンテンツの最後に到達したかを判断することはできますが、この知識をどのように適用して目的の効果を達成するかがわかりません。ありがとう。

// Inner (blue) scroll view bounds checking
if (scrollView.contentOffset.y + scrollView.frame.size.height > scrollView.contentSize.height) { ... }

// Outer (red) scroll view bounds checking
if (scrollView.contentOffset.y < 0) { ... }
4

2 に答える 2

19

うん。私はあなたのために気の利いたトリックを持っています。

赤い輪郭を描いたビューをスクロールビューにする代わりに、画面いっぱいに表示する通常の UIView にします。そのビューで、スクロール ビュー (テーブル ビュー) とイメージ ビューを図のようにレイアウトします。

ルート ビューの境界を埋める (つまり、画面全体を埋める) スクロール ビューを、他のすべてのスクロール ビューおよびイメージ ビューの上に配置します。このビューのコンテンツ サイズを、スクロールするすべてのビューのコンテンツの高さの合計に設定します。つまり、他のすべてのビューの上に非表示のスクロールビューがあり、そのコンテンツ サイズの高さは、内側のスクロールビュー (テーブルビュー) コンテンツ サイズの高さ + 画像ビュー サイズの高さです。

階層は次のようになります。

ここに画像の説明を入力

次に、非常に背の高いコンテンツサイズで作成したこのスクロールビューの上に、デリゲートをビューコントローラーにします。実装scrollViewDidScrollすると、いくつかの魔法がかかります。

Scrollviews は基本的に、境界の原点を運動量などのファンキーな式で調整することによってスクロールします。したがって、このscrollviewDidScroll方法では、基になるビューの境界を調整するだけです。

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{

    //the scroll view underneath (in the container view) will have a max content offset equal to the content height
    //but minus the bounds height
    CGFloat maxYOffsetForUnderScrollView = self.underScrollView.contentSize.height - self.underScrollView.bounds.size.height;

    CGRect scrolledBoundsForContainerView = self.view.bounds;

    if (scrollView.contentOffset.y <= maxYOffsetForUnderScrollView) {
        //in this scenario we are still within the content for the underScrollView
        //so we make sure the container view is scrolled to the top and set the offset for the contained scrollview
        self.containerView.bounds = scrolledBoundsForContainerView;
        self.underScrollview.contentOffset = scrollView.contentOffset;
        return;
    }

    //in this scenario we have scrolled throug the entirety of the contained scrollview
    //set its offset to the max and change the bounds of the container view to scroll everything else.
    self.underScrollView.contentOffset = CGPointMake(0, maxYOffsetForUnderScrollView);

    scrolledBoundsForContainerView.origin.y = scrollView.contentOffset.y - maxYOffsetForUnderScrollView;
    self.containerView.bounds = scrolledBoundsForContainerView;
}

scrollViewDidScroll はアニメーションのすべてのフレームで呼び出されるため、含まれているビューのこの誤ったスクロールが非常に自然に見えることがわかります。

ちょっと待って!私はあなたが言うのを聞きます。上部のスクロール ビューはすべてのタッチをインターセプトするようになり、その下のビューもタッチする必要があります。それについても興味深い解決策があります。

一番上のスクロールビューをどこか画面外に設定します(つまり、フレームを画面外に設定しますが、同じサイズです)。次に、メソッドでスクロールビューをメインビューにviewDidLoad追加します。panGestureRecogniserこれは、実際に画面を表示しなくても、iOS の自然なスクロールの勢いなどをすべて取得できることを意味します。含まれているスクロール ビューは、パン ジェスチャ レコグナイザーも呼び出されるため (UIEvent 処理とは異なる動作をするため)、おそらくおかしなことになるので、それを削除する必要があります。

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self.view addGestureRecognizer:self.scrollview.panGestureRecognizer];

    [self.underScrollview removeGestureRecognizer:self.underScrollView.panGestureRecognizer];

    //further code to set up content sizes and stuff
}

これを作るのは楽しかったので、github のサンプル プロジェクトへのリンクを以下に示します: https://github.com/joelparsons/multipleScrollers

編集:

どこに配置しても画面外にあるときに上部のスクロールビューのスクロールバーを表示するには、次のscrollIndicatorInsetsように作成されたインセットに設定できます。

CGPoint scrollviewOrigin = self.scrollview.frame.origin;
self.scrollview.scrollIndicatorInsets = UIEdgeInsetsMake(-scrollviewOrigin.y,0,scrollviewOrigin.y,scrollviewOrigin.x);

*スクロールビューは適切な高さでなければならないことに注意してください。

そして、バーをスクロールビューの可視境界の外側に描画するには、境界へのクリップをオフにする必要があります

self.scrollview.clipsToBounds = NO;
于 2012-11-04T19:34:30.680 に答える
0

ああ、神様。ジャックスラッシュ、あなたは私の命を救った。

私の場合、3 段階のスクロール ビューを使用する必要があります。

  1. 親スクロール ビュー
    • 子の 1 つとしてスクロール ビューを持つ
  2. 子スクロールビュー:下図では「説明スクロールビュー / レビューテーブルビュー / 情報スクロールビュー」と表示)
  3. 非表示のスクロール ビュー
    • これは、「親スクロール ビュー」と「子スクロール ビュー」に独自のコンテンツ オフセットを配布します。
    • フラット化されたコンテンツ全体のコンテンツ サイズを持つ

ビュー階層のスクリーンショット

ビュー階層を設定したら、フラット化されたコンテンツ サイズ全体を同期し、コンテンツ オフセットを適切に分散するだけです。

    Observable.combineLatest(
        overviewStackView.rx.observe(CGRect.self, #keyPath(UIStackView.frame)).unwrap(),
        explanationScrollView.rx.observe(CGSize.self, #keyPath(UIScrollView.contentSize)).unwrap()
      )
      .subscribe(onNext: { [weak self] overviewStackViewFrame, explanationScrollViewContentSize in
        guard let self = self else { return }
        let totalContentHeight = overviewStackViewFrame.height + self.segmentedControl.frame.height + explanationScrollViewContentSize.height
        self.hiddenScrollView.contentSize.height = totalContentHeight
      })
      .disposed(by: disposeBag)

    hiddenScrollView.rx.didScroll
      .subscribe(onNext: { [weak self] _ in
        guard let self = self else { return }
        let currentHiddenScrollViewOffsetY = self.hiddenScrollView.contentOffset.y
        let parentScrollViewMaxOffsetY = self.overviewStackView.frame.height
        let expectedChildScrollViewOffsetY = max(currentHiddenScrollViewOffsetY - parentScrollViewMaxOffsetY, 0)
        self.parentScrollView.contentOffset.y = min(parentScrollViewMaxOffsetY, currentHiddenScrollViewOffsetY)
        self.explanationScrollView.contentOffset.y = expectedChildScrollViewOffsetY
      })
      .disposed(by: disposeBag)
于 2020-08-19T10:16:18.313 に答える