web-dev-qa-db-ja.com

サイズクラスを使用してプログラムで2つの異なるレイアウトを実装する

4つのボタンレイアウトがあります。ポートレートでは、上下に表示する必要があります。ランドスケープでは、それぞれ2つのボタンがある2つの列になります。

コードにボタンを実装します-本当にシンプルなもの:

UIButton *btn1 = [[UIButton alloc] init];
[self.view addSubview: btn1]; 

UIButton *btn2 = [[UIButton alloc] init];
[self.view addSubview: btn2]; 

UIButton *btn3 = [[UIButton alloc] init];
[self.view addSubview: btn3]; 

UIButton *btn4 = [[UIButton alloc] init];
[self.view addSubview: btn4]; 

NSDictionary *views = NSDictionaryOfVariableBindings(btn1, btn2, btn3, btn4);

[btn1 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn2 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn3 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn4 setTranslatesAutoresizingMaskIntoConstraints:NO];

// portrait constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn1]-(50)-|"
                                                                 options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn2]-(50)-|"
                                                                 options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn3]-(50)-|"
                                                                 options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn4]-(50)-|"
                                                                 options:0 metrics:nil views:views]];

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn1]-[btn2]-[btn3]-[btn4]-(50)-|"
                                                                 options:0 metrics:nil views:views]];

これは明らかにポートレートレイアウトの設定です。 iPadとiPhoneのそれぞれの向きで特定のケースを作成するために、デバイスとその向きを決定していました。しかし、今はサイズクラスを使用することになっています。サイズクラスが「コンパクト」であるかどうかを判断し、適切な制約を設定するにはどうすればよいですか?

38
Joseph

それまでの間、私は良い解決策を見つけました。この質問には非常に多くの賛成票があるため、すぐに説明すると思いました。 WWDCセッションからこのソリューションに触発されました。

Swiftに移動したので、コードはSwift-Obj-Cでも概念は同じです。

まず、3つの制約配列を宣言することから始めます。

 // Constraints
 private var compactConstraints: [NSLayoutConstraint] = []
 private var regularConstraints: [NSLayoutConstraint] = []
 private var sharedConstraints: [NSLayoutConstraint] = []

そして、それに応じて制約を満たします。つまり、viewDidLoadから呼び出す別の関数でこれを行うことも、viewDidLoadで直接行うこともできます。

sharedConstraints.append(contentsOf: [
     btnStackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
    ...
])

compactConstraints.append(contentsOf: [
     btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7),
    ...
])

regularConstraints.append(contentsOf: [
     btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.4),
    ...
])

重要な部分は、サイズクラスの切り替えと、適切な制約の有効化/無効化です。

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

    super.traitCollectionDidChange(previousTraitCollection)

    if (!sharedConstraints[0].isActive) {
       // activating shared constraints
       NSLayoutConstraint.activate(sharedConstraints)
    }


    if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
        if regularConstraints.count > 0 && regularConstraints[0].isActive {
            NSLayoutConstraint.deactivate(regularConstraints)
        }
        // activating compact constraints
        NSLayoutConstraint.activate(compactConstraints)
    } else {
        if compactConstraints.count > 0 && compactConstraints[0].isActive {
            NSLayoutConstraint.deactivate(compactConstraints)
        }
        // activating regular constraints
        NSLayoutConstraint.activate(regularConstraints)
    }
}

私は、制約が問題の制約に適合しないことを知っています。しかし、制約自体は無関係です。主なことは、サイズクラスに基づいて2組の制約を切り替える方法です。

お役に立てれば。

37
Joseph

ビューの特性コレクションを調べて、水平および垂直サイズクラスを決定できます。

if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    ...
}

traitCollectionDidChange:メソッドは、自動回転により特性が変化したときに自動的に呼び出されます。

詳細については、「 ITraitCollection Class Reference 」および「 ITraitEnvironment Protocol Reference 」を参照してください。

35
user4151918

受け入れられた答えのSwift 4コード:

if (self.view.traitCollection.horizontalSizeClass == .compact) {
...
}
10
RemyDCF

traitCollectionはiOS8でのみ機能します。そのため、アプリはiOS7でクラッシュします。以下のコードを使用して、iOS7とiOS8の両方をサポートします

if ([self.view respondsToSelector:@selector(traitCollection)]){

    if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    ...
    }

}
2
Kirit Vaghela