web-dev-qa-db-ja.com

UIViewControllerに戻るとUIScrollViewのOriginが変更されます

さまざまなサブビューを含むUIViewControllerを含むストーリーボードのシーンとしてUIScrollViewサブクラスがあります。サブビューの1つは、別のシーンUIButtonサブクラスにセグメンテーションするUIViewControllerです。ビューから戻ったとき(Navigation ControllerスタックからUIViewControllerをポップ)、スクロールビューのOriginが何らかの形で変更されていることがわかりますが、contentsizecontentoffsetは正しい。

また興味深いのは、アプリにタブバーがあることです。タブビューでそのビューに戻ると、スクロールビューはオフセット(0、0)で正しく設定されます。

ストーリーボードにはほとんどすべてのコードがあるため、基本的にこのプロセスに関係するコードはありません。ストーリーボードを使用するのはかなり新しいので、何が間違っているのかわかりませんが、何をするのかわかりません。それが何であるかについてのアイデアはありますか?おそらくサイジングの問題や制約ですか?

57
Peter Jacobs

実際、そのコード行をviewDidDisappearに配置し、ビューが再表示されたときにオフセットを記憶するように、その前にこの行を追加しました

 self.contentOffset = self.scrollView.contentOffset;

と同様

 - (void)viewDidLayoutSubviews { 
       self.scrollView.contentOffset = self.contentOffset; 
 }
11
Peter Jacobs

IOS 7/8/9シンプルself.automaticallyAdjustsScrollViewInsets = NO;私の場合、問題を解決しました。

92
Alex

これを、表示するView ControllerのviewWillAppearで試してください:

 self.scrollView.contentOffset = CGPointMake(0, 0);

編集:Peterのコードも追加すると、次のように最適な結果が得られます

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:YES];
    self.scrollView.contentOffset = CGPointMake(0, 0);
}

プラス

- (void)viewWillDisappear:(BOOL)animated {  
    self.recentContentOffset = self.scrollView.contentOffset;
    [super viewWillDisappear:animated];
}

そして

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.scrollView.contentOffset = CGPointMake(0, self.recentContentOffset.y);
}

元のスクロール位置に戻り、視覚的な副作用がなく、スクロールビュー自体が戻ってきた後、スーパービュー(問題でした)に正しく配置されます。

30
MacMark

同様の問題がありました。viewControllerを閉じた後、tableViewのcontentOffsetが(0、-64)に変更されました。

私の解決策は少し奇妙で、他のすべての答えを試してみましたが成功しませんでした。私の問題を解決した唯一のことは、.xibのコントロールツリーでtableViewの位置を切り替えることでした

これは、次のような親ビューの最初のコントロールでした:

before

ImageViewの直後にtableViewを移動し、動作しました:

after

テーブルビューを最初の位置に置くことが問題の原因であり、テーブルビューを別の位置に移動すると問題が解決したようです。

P.D. autoLayoutもストーリーボードも使用していません

これが誰かを助けることを願っています!

5
Chuy47

CollectionViewを使用していますが、同様の問題がありました。 iOS 11の場合:サイズインスペクターに「コンテンツインセット」があります。それを「Never」に設定します。これで問題は解決しました。これが誰かの役に立つことを願っています。

目標C:

if (@available(iOS 11, *)) {
   [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}

迅速:

if #available(iOS 11, *) {
UIScrollView.appearance().contentInsetAdjustmentBehavior = .never
}
5
Joseph Francis

IOS 11では、View Controllerをポップした後に戻ると、以前のViewControllerのTableViewがコンテンツのインセットを自動的に調整し、TableViewのコンテンツが突然上からジャンプするという同様の問題に直面しました。次のソリューションはiOS 11で機能しました。

ストーリーボードで、tableviewを選択し、属性インスペクターに移動して、[行の高さ]および[見積もり]フィールドの[自動]のチェックを外します。また、コンテンツのインセットを「自動」から「なし」に変更します。

[Table view]

2
Nithin

残念ながら、PeterとMacMarkの提案はうまくいきませんでした(Xcode 5 w/auto-layout)。解決策は、ストーリーボードに移動し、View Controllerを選択して、Reset to Suggested Constraints in View Controller

enter image description here

1
Kyle Clegg

私は最近、同様の問題に関する質問を投稿しましたが、異なるコンテキストで: フレームは、モーダルビューコントローラーを閉じた後、自動レイアウト制約を反映しません

私の場合、スクロールビュー自体の原点ではなく、スクロールビュー内のカスタムコンテナビューの原点が移動します。自動レイアウトに関して、カスタムコンテナビューはスクロールビューの4つの側面に固定されます。

コンテナビューは、IBではなくプログラムで作成および構成されます。コンテナビューの制約は変更されていませんが、モーダルビューコントローラを閉じると、コンテナビューのOriginは移動します。制約とフレームの間のこの切断は不可解です。

さらに注目に値するのは、関連するすべての制約を削除して再度追加した後、Originの変位問題が残っていたことです。

同様の解決策を思いつきました:現在のコンテンツオフセットの値を保存し、コンテンツオフセットを「ゼロ」に設定し、システムにビューをスワップさせ、コンテンツオフセット(つまり、コンテンツオフセットダンス)を復元します。

ただし、私の場合は、問題を解決するために追加の手順を実行する必要がありました。私のスクロールビューは、(古いWWDCビデオでデモされた)tilePagesメソッドからサブビューを取得するページングスクロールビューです。スクロールビューのコンテンツオフセットを変更すると、UIScrollViewDelegateのscrollViewDidScroll:メソッドを介してtilePagesメソッドがトリガーされます。 content-offsetダンスを行っている間にtilePagesをオフにするフラグを設定する必要がありました。そうしないと、アプリがクラッシュしました。

IOS 7にアップグレードするときに、コンテンツオフセットダンスのコードを削除できれば幸いです。

0
bilobatum
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7) {
        _isRecoredforios6 = YES;
        _recordedOffsetforios6 = _scrollView.contentOffset;
        _recordedSizeforios6 = _scrollView.contentSize;
        _scrollView.contentOffset = CGPointZero;
    }

}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    if (_isRecoredforios6) {
        _isRecoredforios6 = NO;
        _scrollView.contentSize = _recordedSizeforios6;
        _scrollView.contentOffset = _recordedOffsetforios6;
    }
}

このios6バグを修正しましたが、これらのコードを使用して解決できます。 Scrollviewで発生したバグを解決しました。何より私の友人に感謝します!

0
J.Mi

SOの至る所に投稿されているさまざまなソリューションの組み合わせを使用して、このサブクラスを思い付きました。

// Keeps track of the recent content offset to be able to restore the
// scroll position when a modal viewcontroller is dismissed
class ScrollViewWithPersistentScrollPosition: UIScrollView {

    // The recent content offset for restoration.
    private var recentContentOffset: CGPoint = CGPoint(x: 0, y: 0)

    override func willMove(toWindow newWindow: UIWindow?) {
        if newWindow != nil {
            // save the scroll offset.
            self.recentContentOffset = self.contentOffset
        }
        super.willMove(toWindow: newWindow)
    }

    override func didMoveToWindow() {
        if self.window != nil {
            // restore the offset.
            DispatchQueue.main.async(execute: {
                // restore it
                self.contentOffset = self.recentContentOffset
            })
        }
        super.didMoveToWindow()
    }
 }

IOS 11.2/Xcode 9.2でテスト済み。

0
KBog

現在の回答に従うときの問題の続き:

表示中のView Controllerを残さずに、表示中のView Controllerを開こうとすると、問題が残りました。これがexact stepsを投稿し、それが私のソリューションをもたらした理由です。

  1. したがって、I reset StoryboardのcollectionViewの制約は、presentingViewControllerのメインビューに固定されていることを確認します。

  2. 追加self.view.translatesAutoresizingMaskIntoConstraints = YES;表示するView ControllerのviewDidLoad内。

  3. そしてstored、プライベートには、モーダル表示されたView Controllerの外観の前のコレクションビューのcontentOffset:

    (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
    
        self.contentOffset = self.collectionView.contentOffset;
        self.collectionView.contentOffset = CGPointZero;
    }
    
    - (void)viewDidLayoutSubviews {
         [super viewDidLayoutSubviews];
         self.collectionView.contentOffset = self.contentOffset;
    }
    
0
ChrisHaze

[不透明バーの下のエッジを延長]オプションをチェックしようとしましたか?ストーリーボード内のコントローラー属性インスペクターにあります。

XCode 9 iOS 11を使用して、私にとってはトリックをしました。

enter image description here

0
Swift Rabbit

最近、私はこれらのコードを使用して解決できるこのバグに遭遇しました:

// fix ios 6 bug begin
- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];

    if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7) {
        isRecoredforios6 = YES;
        recordedOffsetforios6 = self.tableView.contentOffset;
        recordedSizeforios6 = self.tableView.contentSize;
    }
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    if (isRecoredforios6) {
        isRecoredforios6 = NO;
        self.tableView.contentSize = recordedSizeforios6;
        self.tableView.contentOffset = recordedOffsetforios6;
    }
}
// fix ios 6 bug end

ピーター・ジェイコブスに感謝!

0
Trouble maker

上記のどれもうまくいきませんでした。代わりに、独自のカスタムプッシュ/プルアニメーションを実行することができました。

最初に、プッシュシナリオを実装するこのクラスを追加します

class PushAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 0.5
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

    // get reference to our fromView, toView and the container view that we should perform the transition in
    let container = transitionContext.containerView
    let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
    let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!

    // start the toView to the right of the screen
    var frame = toView.frame
    frame.Origin.x = container.frame.width
    toView.frame = frame

    // add the both views to our view controller
    container.addSubview(toView)
    container.addSubview(fromView)

    // get the duration of the animation
    let duration = self.transitionDuration(using: transitionContext)

    // perform the animation!
    UIView.animate(withDuration: duration, animations: {

        var frame = fromView.frame
        frame.Origin.x = -container.frame.width
        fromView.frame = frame

        toView.frame = container.bounds

    }, completion: { _ in
        // tell our transitionContext object that we've finished animating
        transitionContext.completeTransition(true)

    })
}
}

次に、ポップシナリオを実装するこのクラスを追加します

import Foundation
import UIKit

class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 0.5
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    // get reference to our fromView, toView and the container view that we should perform the transition in
    let container = transitionContext.containerView
    let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
    let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!

    // set up from 2D transforms that we'll use in the animation
    let offScreenRight = CGAffineTransform(translationX: container.frame.width, y: 0)

    // start the toView to the right of the screen
    var frame = toView.frame
    frame.Origin.x = -container.frame.width
    toView.frame = frame

    // add the both views to our view controller
    container.addSubview(toView)
    container.addSubview(fromView)

    // get the duration of the animation
    let duration = self.transitionDuration(using: transitionContext)

    // perform the animation!
    UIView.animate(withDuration: duration, animations: {

        fromView.transform = offScreenRight
        toView.frame = container.bounds

    }, completion: { _ in
        // tell our transitionContext object that we've finished animating
        transitionContext.completeTransition(true)

    })
}
}

次に、この行をviewDidLoadメソッドに追加して、デフォルトのNavigationController Delegateを変更します

self.navigationController?.delegate = self

そして魔法を参照してください:)

0
Elsammak

上記のいずれも役に立たない場合に備えて、私はこの問題を抱えていましたが、私にとっての問題は次のコードがあったことでした。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    tableView.reloadData()
}

このreloadData()行をコメントアウトすると、問題が修正されました。この行は必要なかったので、なぜ追加したのかわからない!

このソリューションが他の人の助けになることを願っています。

0
teh_raab

多くのことを試した後:

  • 自動的にAdjustsScrollViewInsetsをfalseに設定して、問題がナビゲーションバーにあったかどうかを確認します。
  • セルの高さを固定して遊ぶ...
  • セルの高さをキャッシュしています...
  • プロパティをテーブルビューオフセットで保持し、キャッシュして、viewWillAppearで設定します...
  • 等.

この問題はestimatedRowHeightの値に関するもので、150pxになるはずの50pxに設定されていることに気付きました。値を更新すると問題が修正され、Table Viewは同じオフセットを保持するようになりました。

0
jomafer