web-dev-qa-db-ja.com

activateConstraints:およびdeactivateConstraints:IBで作成されたコンストレイントの回転後に永続化しない

新しいNSLayoutConstraintメソッドactivateConstraints:およびdeactivateConstraints: IBで作成された制約では正しく機能しないようです(コードで作成された制約では正しく機能します)。 2つの制約のセットを持つ1つのボタンで簡単なテストアプリを作成しました。インストールされている1つのセットには、centerXとcenterYの制約があり、アンインストールされているもう1つのセットには、上と左の制約(定数10)があります。 buttonメソッドは、これらの制約セットを切り替えます。これがコードです、

@interface ViewController ()
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *uninstalledConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *installedConstraints;
@end

@implementation ViewController


- (IBAction)switchconstraints:(UIButton *)sender {
    [NSLayoutConstraint deactivateConstraints:self.installedConstraints];
    [NSLayoutConstraint activateConstraints:self.uninstalledConstraints];
}


-(void)viewWillLayoutSubviews {
    NSLog(@"installed: %@    uninstalled: %@", ((NSLayoutConstraint *)self.installedConstraints[0]).active ? @"Active" : @"Inactive", ((NSLayoutConstraint *)self.uninstalledConstraints[0]).active ? @"Active" : @"Inactive");

}

アプリが起動すると、ボタンはインストールされた制約によって定義された正しい中央の位置にあります。ボタンのアクションメソッドでアクティブ化/非アクティブ化を実行した後、ボタンは新しい位置に正しく移動しますが、ビューを横向きに回転すると、最初に定義された位置に戻ります(ログには、新しくアクティブ化されたセットがアクティブです)。回転して縦向きに戻すと、ボタンは最初の位置(画面の中央)に留まり、ログには、最初の制約セットがアクティブで、アクティブにした制約が非アクティブとして表示されます。

問題は、これはバグですか、またはこれらのメソッドはIB定義の制約でこのように機能するはずがないのですか?

27
rdelmar

問題は、ストーリーボードの「アンインストールされた」制約と矛盾したことをしていることです。それらはありますが、ありません。 「アンインストール」制約は、サイズクラスでのみ使用できます。 Xcodeにコンストレイントをスワップさせる自動的にローテーションする場合に使用します。 Xcodeはあなたがやっていることに対応できません。ただし、コードで2つ目の制約セットを作成すると、すべてが正常に機能します。

だから、これを行います。 2つの「アンインストールされた」制約を削除し、uninstalledConstraintsコンセントを削除します。次に、ビューコントローラのコード全体を次のコードに置き換えます。

@property (strong, nonatomic) NSMutableArray *c1;
@property (strong, nonatomic) NSMutableArray *c2;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *installedConstraints;
@property (weak,nonatomic) IBOutlet UIButton *button;
@end

@implementation ViewController {
    BOOL did;
}

- (void)viewDidLayoutSubviews {
    NSLog(@"did");
    if (!did) {
        did = YES;
        self.c1 = [self.installedConstraints mutableCopy];
        self.c2 = [NSMutableArray new];
        [self.c2 addObject:
         [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTopMargin multiplier:1 constant:30]];
        [self.c2 addObject:
         [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeadingMargin multiplier:1 constant:30]];
    }
}

- (IBAction)switchconstraints:(UIButton *)sender {
    [NSLayoutConstraint deactivateConstraints:self.c1];
    [NSLayoutConstraint activateConstraints:self.c2];
    NSMutableArray* temp = self.c1;
    self.c1 = self.c2;
    self.c2 = temp;
}

ボタンを繰り返し押します。ご覧のとおり、2つの位置の間をジャンプします。アプリを回転させます。ボタンはそのままです。

30
matt

私も同じような状況に直面しました。

インターフェイスビルダーでNSLayoutConstraintsの2つのセットを作成しました。 1セットの各ケース。一方のセットは「インストール」され、もう一方はインストールされませんでした。

ケースを切り替えると、対応するレイアウト制約のセットがアクティブになり、もう1つは非アクティブ化されました。前後に回転するQusteionで説明されているように、正しく動作しません。

インターフェースビルダーに両方のセットをインストールすることでこれは解決されます。警告を取り除くために、私は2番目のセットに優先順位の低い優先順位(999)を使用しました。これでうまくいきました。

ところで:Strangは、別のビューコントローラーで「インストール済み/未インストール」アプローチを使用しましたが、うまくいきました。

機能していないケースでは、ビューコントローラがコンテナビューに埋め込まれていました。おそらくそれが理由でした。

13
Christian1313

あなたはcanこのワークフローを使用しますが、これは現在Appleの人々がサポートしているユースケースではないことは明らかです。

トリックはnotアンインストール制約(@mattも指摘したとおり)ですが、レイアウトが発生する前にUIViewControllerサブクラスのviewDidLoad()で制約を非アクティブ化します(競合する制約により問題が発生します)。

私は現在その方法を使用しており、完全に機能します。もちろん理想的には、制約のグループを視覚的に作成し、それらをアクティブ化および非アクティブ化するだけでそれらをキーフレームのように使用することができます(その間に無料のアニメーションを使用):)

3
Aral Balkan