web-dev-qa-db-ja.com

iOS自動レイアウト親ビューを埋めるために垂直方向に等しいスペース

12 UITextFieldsのViewControllerがあります。

3.5インチディスプレイに非常によく適合します。

enter image description here

すべてのUITextFieldsがUIView全体をカバーするように、iPhone 5(4インチディスプレイ)用に設定する必要があります。

自動レイアウトでこれを実行しようとしていますが、正しく機能していません。

enter image description here

これは私のコードです:

_- (void) viewWillLayoutSubviews
{
    int h = txt1.bounds.size.height * 12;

    float unusedHorizontalSpace = self.view.bounds.size.height - h ;

    NSNumber* spaceBetweenEachButton=  [NSNumber numberWithFloat: unusedHorizontalSpace / 13 ] ;

    NSMutableArray *constraintsForButtons = [[NSMutableArray alloc] init];

    [constraintsForButtons addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat: @"V:|-50-[txt1(==30)]-(space)-[txt2(==txt1)]-(space)-[txt3(==txt1)]-(space)-[txt4(==txt1)]-(space)-[txt5(==txt1)]-(space)-[txt6(==txt1)]-(space)-[txt7(==txt1)]-(space)-[txt8(==txt1)]-(space)-[txt9(==txt1)]-(space)-[txt10(==txt1)]-(space)-[txt11(==txt1)]-(space)-[txt12]-(space)-|"
                                                                                        options: NSLayoutFormatAlignAllCenterX
                                                                                        metrics: @{@"space":spaceBetweenEachButton}
                                                                                          views: NSDictionaryOfVariableBindings(txt1,txt10,txt11,txt12,txt2,txt3,txt4,txt5,txt6, txt7,txt8,txt9)]];

    [self.view addConstraints:constraintsForButtons];
}
_

[txt12(==txt1)]を実行すると、3.5インチの画面と同じように表示され、下にスペースが残ります。

どこを間違えているのですか?

14
CRDave

自動レイアウトでこれを行うには、テキストフィールド間のスペースを埋めるために追加のビューを作成する必要があります。

自動レイアウト制約は基本的に線形方程式A = m * B + cであることを思い出してください。 Aは1つのビューの属性(たとえば、viewAの下端のY座標)であり、Bは別のビューの属性(たとえば、のY座標)です。 viewBのトップエッジ)。 mcは定数です。したがって、たとえば、viewAviewBをレイアウトして、viewAの下部とviewBの上部の間に30ポイントがあるようにするには、次のようになります。 mが1で、cが-30である制約を作成します。

あなたが抱えている問題は、13の異なる制約にわたってcに同じ値を使用し、自動レイアウトでそのc値を計算することです。自動レイアウトではそれができません。直接ではありません。自動レイアウトは、ビューの属性のみを計算できます。 mおよびc定数を計算することはできません。

自動レイアウトでビューを必要な場所に配置する方法があります。テキストフィールド間のスペースを追加の(非表示の)ビューとして具体化します。テキストフィールドが3つしかない例を次に示します。

example layout

各スペーサーの上端をその上のテキストフィールドの下端に固定するための制約を作成します。また、各スペーサーの下端をその下のテキストフィールドの上端に固定するための制約を作成します。最後に、各スペーサーが最上部のスペーサーと同じ高さになるように強制する制約を作成します。

設定するには、2つのインスタンス変数が必要です。テキストフィールドの配列(上から下の順序)と、最上部のスペーサービューへの参照です。

@implementation ViewController {
    NSMutableArray *textFields;
    UIView *topSpacer;
}

スタックオーバーフローの回答でxibを表示するのは難しいため、コードでテキストフィールドとスペーサーを作成します。 viewDidLoadで開始します:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addTextFields];
    [self addSpacers];
}

自動レイアウトを使用するため、システムが追加の制約を作成しないように、translatesAutoresizingMaskIntoConstraintsをオフにする必要があります。

各テキストフィールドを作成し、それにダミーテキストを与え、その水平位置とサイズの制約を設定します。

- (void)addTextFields {
    textFields = [NSMutableArray array];
    for (int i = 0; i < 12; ++i) {
        [self addTextField];
    }
}

- (void)addTextField {
    UITextField *field = [[UITextField alloc] init];
    field.backgroundColor = [UIColor colorWithHue:0.8 saturation:0.1 brightness:0.9 alpha:1];
    field.translatesAutoresizingMaskIntoConstraints = NO;
    field.text = [field description];
    [self.view addSubview:field];
    [field setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [field setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[field]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(field)]];
    [textFields addObject:field];
}

スペーサーの作成にもループを使用しますが、上部と下部のスペーサーをスーパービューに固定する必要があるため、上部と下部のスペーサーは中央のスペーサーとは異なる方法で作成します。

- (void)addSpacers {
    [self addTopSpacer];
    for (int i = 1, count = textFields.count; i < count; ++i) {
        [self addSpacerFromBottomOfView:textFields[i - 1]
            toTopOfView:textFields[i]];
    }
    [self addBottomSpacer];
}

上部のスペーサーを作成し、その拘束を設定する方法は次のとおりです。その上端はスーパービューに固定され、下端は最初の(最上部の)テキストフィールドに固定されます。上部のスペーサーをインスタンス変数topSpacerに格納して、他のスペーサーが上部のスペーサーと同じ高さになるように制約できるようにします。

- (void)addTopSpacer {
    UIView *spacer = [self newSpacer];
    UITextField *field = textFields[0];
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[spacer][field]" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer, field)]];
    topSpacer = spacer;
}

実際にスペーサービューを作成する方法は次のとおりです。それはただの隠された眺めです。水平方向のサイズや位置は気にしないので、スーパービューの左右の端に固定するだけです。

- (UIView *)newSpacer {
    UIView *spacer = [[UIView alloc] init];
    spacer.hidden = YES; // Views participate in layout even when hidden.
    spacer.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:spacer];
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"|[spacer]|" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer)]];
    return spacer;
}

2つのテキストビューの間に「中間」スペーサーを作成するには、それを上のテキストフィールドの下端と下のテキストフィールドの上端に固定します。また、その高さを上部スペーサーの高さと等しくなるように制約します。

- (void)addSpacerFromBottomOfView:(UIView *)overView toTopOfView:(UIView *)underView {
    UIView *spacer = [self newSpacer];
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:[overView][spacer(==topSpacer)][underView]" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer, overView, underView, topSpacer)]];
}

下部のスペーサーを作成するには、最後のテキストフィールドとスーパービューに固定します。また、その高さを上部スペーサーの高さと等しくなるように制約します。

- (void)addBottomSpacer {
    UIView *spacer = [self newSpacer];
    UITextField *field = textFields.lastObject;
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:[field][spacer(==topSpacer)]|" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer, field, topSpacer)]];
}

正しく実行すると、次のような結果が得られます。

example code screen shot

完全なサンプルプロジェクトは このgithubリポジトリ にあります。

49
rob mayoff

チェックアウト PureLayout 。これは、コードで自動レイアウト制約を作成するために可能な最も単純でプログラマーに優しいAPIになるように設計されています。

特定の質問に答えて、PureLayoutはビューを分散するための2つの主要なAPIを提供します。1つは各ビュー間の間隔が固定されている(ビューサイズは必要に応じて異なります)、もう1つは各ビューのサイズが固定されている(ビュー間の間隔が異なる)必要に応じて)。後者は、「スペーサービュー」を使用せずに探しているものを実現します。

// NSArray+PureLayout.h

// ...

/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
                                alignedTo:(ALAttribute)alignment
                         withFixedSpacing:(CGFloat)spacing;

/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
                                alignedTo:(ALAttribute)alignment
                            withFixedSize:(CGFloat)size;

// ...
10
smileyborg

ソリューションに関する優れた説明が記載されているdeveloper.Appleのドキュメントを参照してください https://developer.Apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html そのページの間隔とワープを参照してください。これは素晴らしい説明だと思います。したがって、ここで同じことを説明する必要はありません。

[〜#〜]編集[〜#〜]

上記のリンクはAppleによって無効になりました。iOS9以降、これらすべてのソリューションであるStackviewが導入されました。

以前の上記のリンクでは、回答は@robによって提供された回答と同じでした

2
Mehul Thakkar