2013-09-25 10 views
5

iOS 6、ページングUIScrollView、および純粋な自動レイアウトを使用しています。モーダルビューコントローラを無効にした後、フレームに自動レイアウトの制約が反映されない

要約:コンテンツのページをスクロールするView Controllerを作成しました。一部のビューはストーリーボードで作成され、設定されます。ビュー階層は次のとおりです。

- Main view (storyboard) 
    - UIScrollView (storyboard) 
    - content view (programmatically) 
     - subviews representing pages of content (programmatically) 

スクロールビューの制約は、IBで設定されています。ここで私はコード内のコンテンツビューのための制約を設定した方法は次のとおりです。

- (void)viewDidLoad 
{ 
    // ABPageScrollerContentView is a subclass of UIView; it overrides intrinsicContentSize; the size is calculated without referencing the scroll view's dimensions 
    self.contentView = [[ABPageScrollerContentView alloc] init]; 
    self.contentView.translatesAutoresizingMaskIntoConstraints = NO; 
    [self.pageScrollerView addSubview:self.contentView]; 

    // configure constraints between scroll view and content view... 
    UIView *contentView = self.contentView; 
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView); 

    [self.pageScrollerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewsDictionary]]; 
    [self.pageScrollerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewsDictionary]]; 

    // the content view's subviews are added/removed in the tilePages method (not shown); tilePages is called later in the view controller lifecycle... 
} 

、ユーザが編集ボタンをタップすると、別のビューコントローラは、ストーリーボードにセグエを使用してモーダルモードで表示されます。 View Controllerが終了すると、制約が変更されていなくても、システムは不可解にコンテンツビューのフレームを変更するように見えます。

は、私は次のデリゲートメソッドで提示ビューコントローラを却下:

- (void)didExitEditPageViewVC:(id)controller 
{ 
    // update currently displayed page view from data model... 

    // logged content view frame = (0, 0; 1020, 460) 

    [self dismissViewControllerAnimated:YES completion:^{ 

     // logged content view frame = (-170, 0; 1020, 460) 
    }]; 
} 

が、私は、フレームの原点のx成分が0から-170に変更する方法を理解していません。この制約は、View Controllerを閉じる前と後で同じです。完了:ここ

は右dismissViewControllerAnimatedを呼び出す前に、フレームと制約である方法:提示ビューコントローラが再表示された後

(lldb) po self.contentView 
$0 = 0x1ede2b40 <AEBPageScrollerContentView: 0x1ede2b40; frame = (0 0; 1020 460); layer = <CALayer: 0x1edd6f00>> 

(lldb) po self.pageScrollerView.constraints 
$1 = 0x1ed076c0 <__NSArrayM 0x1ed076c0>(
<NSLayoutConstraint:0x1ede2980 H:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410)>, 
<NSLayoutConstraint:0x1eded480 H:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410)>, 
<NSLayoutConstraint:0x1edecbc0 V:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410)>, 
<NSLayoutConstraint:0x1ede1040 V:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410)> 
) 

ここでは、フレームと制約です:

contentView = <AEBPageScrollerContentView: 0x1ede2b40; frame = (-170 0; 1020 460); layer = <CALayer: 0x1edd6f00>> 

self.pageScrollerView.constraints = 
(
    "<NSLayoutConstraint:0x1ede2980 H:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410)>", 
    "<NSLayoutConstraint:0x1eded480 H:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410)>", 
    "<NSLayoutConstraint:0x1edecbc0 V:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410)>", 
    "<NSLayoutConstraint:0x1ede1040 V:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410)>" 
) 

コンテンツビューのフレームが突然変更されたのはなぜですか?なぜそれが制約によって指示されるものと一致しないのですか?

hasAmbiguousLayoutの呼び出しが遅延すると、驚いたことにfalseが返されます。例外はスローされません。コンテンツビューは一部オフスクリーンですが、スクロールビューもスクロールします。

いいえどこでスクロールビューのコンテンツサイズを明示的に設定しますか。私はシステムにそれを残す。コンテンツビューには本質的なサイズがあります(コンテンツビューのサイズは問題ありませんが、コンテンツビューの原因は問題です)。

ビューコントローラを閉じる前と後でスクロールビューのコンテンツオフセットが同じです。しかしながら、コンテンツビューの原点のx成分の変位は、コンテンツオフセットに比例する。コンテンツオフセットが大きいほど、モーダルビューコントローラが閉じられた後のコンテンツビューの原点のx成分がより負になります。そして、「ゼロ」のコンテンツオフセットでは、x成分はゼロである。したがって、モーダル・ビュー・コントローラがコンテンツの第1のページを見ている間(コンテンツ・オフセットが「ゼロ」のとき)に提示される場合、コンテンツ・コントローラのビュー・コントローラの解雇時に、コンテンツ・ビューのフレームは正しい。 0のコンテンツオフセットは、コンテンツビューのフレームが制約を正しく反映している唯一の状況です。

私は結果のないさまざまな場所でlayoutIfNeededへの呼び出しを挿入しようとしました。

提案がありますか?

答えて

7

は、私が(ちなみにiOS7に固定されている)この問題を回避しますUIScrollViewのサブクラスを作成しました。私はUIScrollViewをサブクラス化する考えが好きです。修正が来たときに簡単に取り外すことができます。プロジェクトをiOS 7にアップグレードしましたが、プロジェクトの別の部分に移りました。このビューコントローラーに戻ると、私は自分の修正版を削除できます。
+1

賢いソリューション: – bilobatum

0

スクロールビューのコンテンツがオフセットがモーダルビューコントローラの解雇があった後に、コンテンツビューのフレームが正しいされた場合のみが「ゼロ」であったので、次のように、私はこの問題を解決:

// presenting view controller's implementation 
self.contentOffsetBeforeModalViewControllerDismissal = self.pageScrollerView.contentOffset; 

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

[self dismissViewControllerAnimated:YES completion:nil]; 

そして結局のところ

- (void)viewDidLayoutSubviews 
{ 
    if (!CGPointEqualToPoint(self.contentOffsetBeforeModalViewControllerDismissal, CGPointZero)) { 

     self.pageScrollerView.contentOffset = self.contentOffsetBeforeModalViewControllerDismissal; 

     self.contentOffsetBeforeModalViewControllerDismissal = CGPointZero; 
    } 
} 

は、私が提示ビューコントローラのviewWillAppearでオフセットコンテンツを復元することができませんでした:それはあまりにも早方法ので、私が提示ビューコントローラのviewDidLayoutSubviews方法でオフセット実際のコンテンツを回復しました。 viewDidAppearのコンテンツオフセットを復元するのは、あまりにも遅かった。自動レイアウトで作業するとき、私は、そのviewDidLayoutSubviewsがタイミングに関してちょうど良いことを発見しました。

これはちょっとしたハックのような感じです。しかし、それは身近なハックです:少しの状態を保存し、システムがそのことをして、その状態のビットを復元する。

残念ながら、別のバグがすぐに浮上しました。 modal viewコントローラを表示したり却下したり、別のページにスクロールしたりして、再度モーダルビューコントローラを表示した場合、モーダルビューコントローラが解除されたときにアプリケーションがクラッシュします。コンソールに、アプリがクラッシュした理由についての情報はありませんでした。 dismissViewControllerAnimated:completion:の呼び出しで例外がスローされました。

この後者の問題は、(スクロールビューのコンテンツオフセットが変更されるたびにトリガーされる)カスタムページタイリングメソッドをオフにすることによって解決されました。ページのタイリング方法は、コンテンツの正しいページが表示され、サブビューをリサイクルするようにします。正しいサブビューが既にロードされているため、コンテンツオフセットダンス中にページタイルを作成する必要はありません。そこでここでは、コンテンツビューの枠を移動させるか、アプリをクラッシュすることなく、モーダルView Controllerを閉じるための最終的な修正があります:

// presenting view controller's implementation 
self.disablePageTiling = YES // flag causes tilePages to do nothing 

self.contentOffsetBeforeModalViewControllerDismissal = self.pageScrollerView.contentOffset; 

self.pageScrollerView.contentOffset = CGPointMake(0, 0); // triggers tilePages, but nothing will happen because of the flag 

[self dismissViewControllerAnimated:YES completion:^{ 
    self.disablePageTiling = NO; 
}]; 

viewDidLayoutSubviewsの実装では、上記と同じまま。

私は、これらの問題をどのような手段でも完全に解決するとは考えていません。スクロール・ビューで自動レイアウトを使用すると、私は悪いコードを書いているという不安な気持ちを一貫して残しています。だから私はさらなる洞察を歓迎する。また、iOS 7がこれらの問題に影響を与えるかどうかについても興味があります。

@interface ConstraintsSafeScrollView : UIScrollView 
@end 

@implementation ConstraintsSafeScrollView { 
    CGPoint _savedContentOffset; 
    UIEdgeInsets _savedContentInset; 
} 

- (void)willMoveToWindow:(UIWindow *)newWindow { 
    if (newWindow) { 
    // Reset the scrollview to the top. 
    [super setContentOffset:CGPointMake(-_savedContentInset.left, -_savedContentInset.top)]; 
    } 
    [super willMoveToWindow:newWindow]; 
} 

// Overridden to store the latest value. 
- (void)setContentOffset:(CGPoint)contentOffset { 
    _savedContentOffset = contentOffset; 
    [super setContentOffset:contentOffset]; 
} 

// Overridden to store the latest value. 
- (void)setContentInset:(UIEdgeInsets)contentInset { 
    _savedContentInset = contentInset; 
    [super setContentInset:contentInset]; 
} 

- (void)didMoveToWindow { 
    if (self.window) { 
    // Restore offset and insets to their previous values. 
    self.contentOffset = _savedContentOffset; 
    self.contentInset = _savedContentInset; 

    } 
    [super didMoveToWindow]; 
} 

@end 
関連する問題