Đồng đều không gian nhiều chế độ xem trong chế độ xem container


279

Giao diện tự động đang làm cho cuộc sống của tôi khó khăn. Về lý thuyết, nó sẽ thực sự hữu ích khi tôi chuyển đổi, nhưng dường như tôi luôn chiến đấu với nó.

Tôi đã thực hiện một dự án demo để cố gắng tìm kiếm sự giúp đỡ. Có ai biết làm thế nào để làm cho khoảng cách giữa các lượt xem tăng hoặc giảm đều mỗi khi chế độ xem được thay đổi kích thước không?

Dưới đây là ba nhãn (cách đều nhau theo chiều dọc):

hình ảnh1

Những gì tôi muốn là cho họ thay đổi kích thước khoảng cách của họ (không phải kích thước chế độ xem) đồng đều khi tôi xoay. Theo mặc định, chế độ xem trên cùng và dưới cùng hướng về phía trung tâm:

hình ảnh 2


có thể đặt ràng buộc từ nhãn này sang nhãn khác và đặt mối quan hệ "lớn hơn hoặc bằng" sẽ hữu ích
BergP

lập trình ví dụ với việc sử dụng phương thức này của NSLayoutConstraint: + (id) ràng buộcWithItem: (id) view1 thuộc tính: (NSLayoutAttribution) attr1 có liên quanBy: (NSLayoutRelation) liên quan đếnItem: (id) view2 (thuộc tính) hằng số: (CGFloat) c
BergP

Tôi đã mở nguồn thay thế UITabBar chung chung để tiếp cận phương pháp này bằng cách sử dụng Bố cục tự động. Bạn có thể kiểm tra các bài kiểm tra để xem cách tôi tạo các ràng buộc Bố cục tự động. GGTabBar
Goles

Câu trả lời:


211

Vì vậy, cách tiếp cận của tôi cho phép bạn làm điều này trong trình xây dựng giao diện. Những gì bạn làm là tạo ra 'lượt xem spacer' mà bạn đã đặt để khớp với độ cao bằng nhau. Sau đó thêm các ràng buộc trên và dưới vào nhãn (xem ảnh chụp màn hình).

nhập mô tả hình ảnh ở đây

Cụ thể hơn, tôi có một ràng buộc hàng đầu đối với 'Chế độ xem Spacer 1' để giám sát với giới hạn chiều cao ở mức ưu tiên thấp hơn 1000 và với Độ cao bằng với tất cả các 'chế độ xem đệm' khác. 'Spacer View 4' có giới hạn không gian phía dưới để giám sát. Mỗi nhãn có các ràng buộc trên và dưới tương ứng với 'chế độ xem miếng đệm' gần nhất.

Lưu ý: Hãy chắc chắn rằng bạn KHÔNG có thêm các ràng buộc không gian trên / dưới trên nhãn của bạn để giám sát; chỉ những người trong 'khung nhìn không gian'. Điều này sẽ thỏa đáng vì các ràng buộc trên và dưới lần lượt nằm trên 'Space View 1' và 'Spacer View 4'.

Duh 1: Tôi đã nhân đôi quan điểm của mình và chỉ đưa nó vào chế độ nằm ngang để bạn có thể thấy rằng nó hoạt động.

Duh 2: 'Lượt xem spacer' có thể đã được minh bạch.

Duh 3: Cách tiếp cận này có thể được áp dụng theo chiều ngang.


10
Những công việc này. Trong thực tế, các khung nhìn của spacer có thể bị ẩn và chúng vẫn sẽ tạo ra bố cục chính xác.
paulmelnikow

Điều này là siêu hữu ích. Như đã nhận xét về việc chỉ đánh dấu các lượt xem spacer là ẩn. Sau đó, bạn vẫn có thể thấy chúng trong bảng phân cảnh / xib của mình, nhưng chúng không hiển thị trong ứng dụng của bạn. Rất đẹp.
shulmey

Tôi đã nhận được nó gần như làm việc sau nhiều giờ cố gắng lặp đi lặp lại. Thật không may, Xcode tiếp tục xóa các ràng buộc từ không gian đến nút và không gian từ trên xuống của tôi, phá vỡ chuỗi giữa các nút và bộ đệm, và thay thế chúng bằng các ràng buộc từ trên không gian đến giám sát tùy ý, vô dụng.
Desty

Cách tiếp cận tương tự cũng phù hợp với tôi trong bố cục ngang - Tôi có các chế độ xem chiều rộng cố định được phân tách bằng các chế độ xem chiều rộng bằng nhau. Thật tuyệt khi tôi không cần phải trộn nó với bất kỳ mã nào. Cảm ơn!
Kamil Nomtek.com

2
Không cần sử dụng miếng đệm. Xem câu trả lời của tôi dưới đây.
smileBot

316

XEM, KHÔNG CÓ SPACERS!

Dựa trên các đề xuất trong phần nhận xét về câu trả lời ban đầu của tôi, đặc biệt là các đề xuất hữu ích của @ Rivera, tôi đã đơn giản hóa câu trả lời ban đầu của mình.

Tôi đang sử dụng gifs để minh họa cách đơn giản này. Tôi hy vọng bạn tìm thấy gifs hữu ích. Chỉ trong trường hợp bạn gặp vấn đề với gifs, tôi đã bao gồm câu trả lời cũ bên dưới với ảnh chụp màn hình đơn giản.

Hướng dẫn:

1) Thêm các nút hoặc nhãn của bạn. Tôi đang sử dụng 3 nút.

2) Thêm một ràng buộc trung tâm x từ mỗi nút vào giám sát:

nhập mô tả hình ảnh ở đây

3) Thêm một ràng buộc từ mỗi nút vào ràng buộc bố cục dưới cùng:

nhập mô tả hình ảnh ở đây

4) Điều chỉnh các ràng buộc được thêm vào # 3 ở trên như sau:

a) chọn ràng buộc, b) xóa hằng số (đặt thành 0), c) thay đổi hệ số nhân như sau: lấy số nút + 1 và bắt đầu ở trên cùng, đặt số nhân làm nútCountPlus1: 1 , rồi nhấn nútCountPlus1 : 2 , và cuối cùng là nútCountPlus1: 3 . (Tôi giải thích nơi tôi đã nhận công thức này từ câu trả lời cũ bên dưới, nếu bạn quan tâm).

nhập mô tả hình ảnh ở đây

5) Đây là bản demo đang chạy!

nhập mô tả hình ảnh ở đây

Lưu ý: Nếu các nút của bạn có chiều cao lớn hơn thì bạn sẽ cần phải bù cho giá trị này trong giá trị không đổi vì ràng buộc là từ dưới cùng của nút.


Câu trả lời cũ


Bất chấp những gì tài liệu của Apple và cuốn sách tuyệt vời của Erica Sadun ( Tự động phân loại bố cục ) nói, có thể xem các không gian đồng đều mà không cần miếng đệm. Điều này rất đơn giản để thực hiện trong IB và mã cho bất kỳ số lượng phần tử nào bạn muốn để không gian đồng đều. Tất cả những gì bạn cần là một công thức toán học gọi là "công thức phần". Nó đơn giản để làm hơn là để giải thích. Tôi sẽ làm hết sức mình bằng cách chứng minh điều đó trong IB, nhưng nó cũng dễ thực hiện bằng mã.

Trong ví dụ trong câu hỏi, bạn sẽ

1) bắt đầu bằng cách đặt từng nhãn để có một ràng buộc trung tâm. Điều này rất đơn giản để làm. Chỉ cần kiểm soát kéo từ mỗi nhãn xuống dưới cùng.

2) Giữ phím shift, vì bạn cũng có thể thêm các ràng buộc khác mà chúng tôi sẽ sử dụng, cụ thể là "không gian dưới cùng cho hướng dẫn bố cục dưới cùng".

3) Chọn "hướng dẫn bố trí không gian từ dưới lên dưới" và "trung tâm theo chiều ngang trong container". Làm điều này cho cả 3 nhãn.

Giữ phím shift để thêm hai ràng buộc này cho mỗi nhãn

Về cơ bản, nếu chúng ta lấy nhãn có tọa độ mà chúng ta muốn xác định và chia nó cho tổng số nhãn cộng với 1, thì chúng ta có một số chúng ta có thể thêm vào IB để có được vị trí động. Tôi đang đơn giản hóa công thức, nhưng bạn có thể sử dụng nó để đặt khoảng cách ngang hoặc cả dọc và ngang cùng một lúc. Nó siêu mạnh!

Dưới đây là số nhân của chúng tôi.

Nhãn1 = 1/4 = 0,25,

Nhãn2 = 2/4 = .5,

Nhãn3 = 3/4 = .75

(Chỉnh sửa: @Rivera nhận xét rằng bạn chỉ cần sử dụng các tỷ lệ trực tiếp trong trường số nhân và xCode để thực hiện phép toán!)

4) Vì vậy, hãy chọn Label1 và chọn ràng buộc dưới cùng. Như thế này: nhập mô tả hình ảnh ở đây

5) Chọn "Mục thứ hai" trong Trình theo dõi thuộc tính.

6) Từ trình đơn thả xuống, chọn "Đảo ngược mục thứ nhất và mục thứ hai".

7) Không có giá trị hằng số và giá trị wC hAny. (Bạn có thể thêm một phần bù ở đây nếu bạn cần).

8) Đây là phần quan trọng: Trong trường số nhân thêm số nhân đầu tiên của chúng tôi 0,25.

9) Trong khi bạn ở đó, đặt "Mục đầu tiên" hàng đầu thành "CenterY" vì chúng tôi muốn tập trung nó vào trung tâm y của nhãn. Đây là cách tất cả những gì nên nhìn.

nhập mô tả hình ảnh ở đây

10) Lặp lại quy trình này cho mỗi nhãn và cắm vào hệ số nhân có liên quan: 0,5 cho Nhãn2 và 0,75 cho Nhãn3. Đây là sản phẩm cuối cùng trong tất cả các định hướng với tất cả các thiết bị nhỏ gọn! Siêu đơn giản. Tôi đã xem xét rất nhiều giải pháp liên quan đến các đoạn mã và bộ đệm. Đây là giải pháp tốt nhất mà tôi từng thấy về vấn đề này.

Cập nhật: @kraftydevil cho biết thêm Hướng dẫn bố cục dưới cùng chỉ xuất hiện trong bảng phân cảnh, không xuất hiện trong xibs. Sử dụng 'Không gian dưới cùng để chứa' trong xibs. Nắm bắt tốt!

nhập mô tả hình ảnh ở đây


2
Tôi vẫn đang vật lộn với nó. Tôi nghĩ bạn phải sử dụng Xcode 6 vì không có tùy chọn 'wC hAny' trong các ràng buộc Xcode 5. Khi tôi làm theo hướng dẫn của bạn, tất cả các cuộc phỏng vấn bị kẹt vào trung tâm hàng đầu.
kraftydevil

4
@kraftydevil Hướng dẫn bố cục dưới cùng của AFAIK chỉ xuất hiện trong bảng phân cảnh, không xuất hiện trong xibs. Sử dụng 'Không gian dưới cùng để chứa' trong xibs.
Timur Kuchkarov

6
Ồ - cũng đáng chú ý rằng IB trong Xcode 6 hỗ trợ hệ số nhân tỷ lệ, vì vậy bạn có thể đưa vào những thứ như "1: 4", "2: 5", "3: 4", v.v.
Stewohn 5/12/14

3
Cố gắng tìm ra cách thực hiện việc này theo chiều ngang - như TextView thứ nhất cách 1/3 bên trái và TextView thứ hai 2/3 (phần còn lại của đường đi) ... ... (EDIT) đã làm được - hãy làm 1/3 đầu tiên (bên trái) Trailing-Space to Right Margin, đảo ngược, zero, gán như trước ...
kfmfe04

3
Đối với khoảng cách ngang, chỉ cần đặt các ràng buộc theo dõi cho giám sát cho mỗi khung nhìn phụ, sau đó đặt hệ số nhân tỷ lệ, ví dụ: 5: 1, 5: 2, 5: 3, v.v ... di chuyển từ trái sang phải.
dùng344977

98

Giải pháp Trình tạo giao diện rất nhanh:

Để bất kỳ số lượng khung nhìn nào được đặt cách đều nhau trong một giám sát, chỉ cần cung cấp cho mỗi ràng buộc "Align Center X to giám sát" cho bố cục theo chiều ngang hoặc "Align Center Y giám sát" cho bố cục dọc và đặt Hệ số nhân thành N:p( GHI CHÚ: một số đã có may mắn hơn với p:N- xem bên dưới )

Ở đâu

N = total number of views

p = position of the view including spaces

Vị trí đầu tiên là 1, sau đó là khoảng trắng, tạo vị trí tiếp theo 3, do đó p trở thành chuỗi [1,3,5,7,9, ...]. Hoạt động cho bất kỳ số lượt xem.

Vì vậy, nếu bạn có 3 lượt xem để thoát ra, có vẻ như thế này:

Minh họa về cách trải đều các lượt xem trong IB

Lưu ý EDIT : Việc lựa chọn N:phoặc p:Nphụ thuộc vào thứ tự quan hệ của ràng buộc căn chỉnh của bạn. Nếu "Mục đầu tiên" là Superview.Center, bạn có thể sử dụng p:N, trong khi nếu Superview.Center là "Mục thứ hai", bạn có thể sử dụng N:p. Nếu nghi ngờ, chỉ cần thử cả hai ... :-)


Giải pháp này sẽ không phản ánh thiết kế nếu bạn chuyển sang ngôn ngữ khác
wod

@wod Ý bạn là gì? Chuyển cái gì sang ngôn ngữ khác? Và những gì phá vỡ chính xác?
Mete

4
Nếu các phần tử không cùng kích thước thì sao? mô hình này sẽ không hoạt động chính xác phải không?
ucangetit

2
Tôi không hiểu làm thế nào giải pháp này là chính xác? Khoảng cách giữa cạnh của màn hình và các yếu tố bên ngoài có khác với khoảng cách giữa các yếu tố bên ngoài và yếu tố trung tâm?
myles

6
Điều này không phù hợp với tôi "như là" Tôi đã phải đảo ngược bội số (ví dụ 3: 1 từ mục đầu tiên chuyển đổi thành 1: 3). Chỉ cần ném nó ra khỏi đó nếu nó giúp bất cứ ai.
Ces

70

Kể từ iOS 9, Apple đã thực hiện điều này rất dễ dàng với (được chờ đợi từ lâu) UIStackView. Chỉ cần chọn các chế độ xem bạn muốn chứa trong Trình tạo giao diện và chọn Trình chỉnh sửa -> Nhúng vào -> Chế độ xem ngăn xếp. Đặt các ràng buộc chiều rộng / chiều cao / lề thích hợp cho chế độ xem ngăn xếp và đảm bảo đặt thuộc tính Phân phối thành 'Khoảng cách bằng nhau':

Tất nhiên, nếu bạn cần hỗ trợ iOS 8 trở xuống, bạn sẽ phải chọn một trong các tùy chọn khác.


Đúng, thật đáng buồn khi điều này không có khả năng tương thích retro, vì vậy cho đến khi ứng dụng của bạn cần hỗ trợ cho phản hồi IOS8 @Mete là tốt nhất hiện nay.
ZiggyST

51

Tôi đã từng đi tàu lượn siêu tốc yêu tự động và ghét nó. Chìa khóa để yêu nó dường như là chấp nhận những điều sau:

  1. Chỉnh sửa trình tạo giao diện và tự động tạo các ràng buộc "hữu ích" gần như vô dụng đối với tất cả trừ trường hợp tầm thường nhất
  2. Tạo các danh mục để đơn giản hóa các hoạt động phổ biến là một trình bảo vệ cuộc sống vì mã rất lặp đi lặp lại và dài dòng.

Điều đó nói rằng, những gì bạn đang cố gắng không đơn giản và sẽ khó đạt được trong trình xây dựng giao diện. Nó là khá đơn giản để làm trong mã. Mã này, trong viewDidLoad, tạo và định vị ba nhãn như thế nào bạn đang yêu cầu chúng:

// Create three labels, turning off the default constraints applied to views created in code
UILabel *label1 = [UILabel new];
label1.translatesAutoresizingMaskIntoConstraints = NO;
label1.text = @"Label 1";

UILabel *label2 = [UILabel new];
label2.translatesAutoresizingMaskIntoConstraints = NO;
label2.text = @"Label 2";

UILabel *label3 = [UILabel new];
label3.translatesAutoresizingMaskIntoConstraints = NO;
label3.text = @"Label 3";

// Add them all to the view
[self.view addSubview:label1];
[self.view addSubview:label2];
[self.view addSubview:label3];

// Center them all horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

// Center the middle one vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];

// Position the top one half way up
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:0.5 constant:0]];

// Position the bottom one half way down
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:1.5 constant:0]];

Như tôi nói, mã này được đơn giản hóa hơn nhiều với một vài phương thức thể loại UIView, nhưng để rõ ràng tôi đã thực hiện nó một cách lâu dài ở đây.

Phạm trù là ở đây cho những người quan tâm, và nó có một phương pháp cho đều khoảng cách một mảng quan điểm dọc theo một trục đặc biệt.


Thưa ngài, là một cứu cánh. Tôi đã không hiểu # 1 cho đến bây giờ. Trình xây dựng giao diện đang khiến tôi phát điên, nhưng tôi nghĩ nó có thể làm tất cả những việc tương tự. Tôi thích sử dụng mã hơn nữa, vì vậy điều này là hoàn hảo và tôi sẽ hiểu đúng về các phương thức thể loại.
nothappybob

Mặc dù chắc chắn khá gần, nhưng điều này không giải quyết được chính xác vấn đề được hỏi (giữ kích thước khung nhìn giống nhau với khoảng cách giữa các lần xem). Giải pháp này là câu trả lời trường hợp chung tinh tế.
smileyborg

21

Hầu hết các giải pháp này phụ thuộc vào việc có một số lượng lớn các mục để bạn có thể lấy mục giữa và căn giữa nó. Điều gì nếu bạn có một số lượng chẵn các mặt hàng mà bạn vẫn muốn được phân phối đều? Đây là một giải pháp tổng quát hơn. Danh mục này sẽ phân phối đồng đều bất kỳ số lượng vật phẩm nào dọc theo trục dọc hoặc trục ngang.

Ví dụ sử dụng để phân phối theo chiều dọc 4 nhãn trong giám sát của họ:

[self.view addConstraints:
     [NSLayoutConstraint constraintsForEvenDistributionOfItems:@[label1, label2, label3, label4]
                                        relativeToCenterOfItem:self.view
                                                    vertically:YES]];

NSLayoutConstraint + EvenDistribution.h

@interface NSLayoutConstraint (EvenDistribution)

/**
 * Returns constraints that will cause a set of views to be evenly distributed horizontally
 * or vertically relative to the center of another item. This is used to maintain an even
 * distribution of subviews even when the superview is resized.
 */
+ (NSArray *) constraintsForEvenDistributionOfItems:(NSArray *)views
                             relativeToCenterOfItem:(id)toView
                                         vertically:(BOOL)vertically;

@end

NSLayoutConstraint + EvenDistribution.m

@implementation NSLayoutConstraint (EvenDistribution)

+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
                           relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
    NSMutableArray *constraints = [NSMutableArray new];
    NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;

    for (NSUInteger i = 0; i < [views count]; i++) {
        id view = views[i];
        CGFloat multiplier = (2*i + 2) / (CGFloat)([views count] + 1);
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
                                                                      attribute:attr
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:toView
                                                                      attribute:attr
                                                                     multiplier:multiplier
                                                                       constant:0];
        [constraints addObject:constraint];
    }

    return constraints;
}

@end

1
Nó hoạt động bằng cách loại bỏ tất cả các ràng buộc và ngay sau đó, thêm các ràng buộc Chiều rộng và Chiều cao cho các đối tượng đó, sau đó gọi các ràng buộc phương
thứcForEvenDistributionOfItems

@carlos_ms Tôi rất muốn xem mã của bạn, bởi vì khi tôi thử với số lượng mục chẵn, mã này hoạt động hoàn hảo khi chuyển hướng thiết bị. Bạn có chắc chắn rằng bạn không vô tình có một số ràng buộc khác (chẳng hạn như các ràng buộc tự động hóa) đang gây ra xung đột?
Ben Dolman

Dưới đây là một ví dụ về việc tạo 4 nhãn và cách đều nhau dọc theo trục dọc: gist.github.com/bdolman/5379465
Ben Dolman

20

Cách chính xác và dễ nhất là sử dụng Stack Views.

  1. Thêm nhãn / lượt xem của bạn vào Chế độ xem ngăn xếp :

nhập mô tả hình ảnh ở đây

  1. Chọn Chế độ xem ngăn xếp và đặt Phân phối thành Khoảng cách bằng nhau :

nhập mô tả hình ảnh ở đây

  1. Thêm khoảng cách vào các ràng buộc lân cận gần nhất vào Khung nhìn và cập nhật khung:

nhập mô tả hình ảnh ở đây

  1. Thêm các ràng buộc về Chiều cao cho tất cả các nhãn (tùy chọn). Chỉ cần cho các chế độ xem không có Kích thước nội tại). Ví dụ, các nhãn không cần ràng buộc về chiều cao và chỉ cần đặt numberOfLines = 3 hoặc 0 chẳng hạn.

nhập mô tả hình ảnh ở đây

  1. Thưởng thức xem trước:

nhập mô tả hình ảnh ở đây


2
Đây chỉ là> = iOS 9.0 vì vậy hãy kiểm tra mục tiêu triển khai của bạn trước khi bạn bắt đầu triển khai :) Thật tuyệt khi thấy điều này đã được thêm vào!
DivideByZer0

1
2018 ngay bây giờ, sử dụng chế độ xem ngăn xếp
Jingshao Chen

17

Kiểm tra thư viện mã nguồn mở PureLayout . Nó cung cấp một vài phương thức API để phân phối các chế độ xem, bao gồm các biến thể trong đó khoảng cách giữa mỗi chế độ xem được cố định (kích thước chế độ xem thay đổi khi cần) và khi kích thước của mỗi chế độ xem được cố định (khoảng cách giữa các chế độ xem thay đổi khi cần). Lưu ý rằng tất cả những điều này được thực hiện mà không cần sử dụng bất kỳ "chế độ xem đệm" nào.

Từ NSArray + PureLayout.h :

// 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;

// ...

Vì tất cả đều là nguồn mở, nếu bạn quan tâm để xem cách đạt được điều này mà không có lượt xem spacer, hãy xem triển khai. (Nó phụ thuộc vào việc tận dụng cả constant multiplier cho các ràng buộc.)


Công việc tốt đẹp! Mặc dù bạn có lẽ nên đổi tên repo để bạn có thể cung cấp nó thông qua cocoapods.
jrturton

9

Tôi đang có một vấn đề tương tự và phát hiện ra bài đăng này. Tuy nhiên, không có câu trả lời nào được cung cấp hiện tại giải quyết vấn đề theo cách bạn muốn. Họ không tạo khoảng cách bằng nhau, mà phân phối trung tâm của nhãn bằng nhau. Điều quan trọng là phải hiểu rằng điều này không giống nhau. Tôi đã xây dựng một sơ đồ nhỏ để minh họa điều này.

Xem khoảng cách minh họa

Có 3 lượt xem, tất cả đều cao 20pt. Sử dụng bất kỳ phương pháp được đề xuất nào đều phân phối đều các trung tâm của các khung nhìn và cung cấp cho bạn bố cục minh họa. Lưu ý rằng tâm y của các khung nhìn được đặt cách đều nhau. Tuy nhiên, khoảng cách giữa giám sát và chế độ xem trên cùng là 15pt, trong khi khoảng cách giữa các lần xem xét chỉ là 5pt. Để có các khung nhìn cách đều nhau, cả hai phải là 10pt, tức là tất cả các mũi tên màu xanh phải là 10pt.

Tuy nhiên, tôi chưa đưa ra một giải pháp chung chung nào. Hiện tại, ý tưởng tốt nhất của tôi là chèn "các chế độ xem khoảng cách" giữa các lần xem và đặt độ cao của các chế độ xem khoảng cách bằng nhau.


1
Tôi đã sử dụng giải pháp của các khung nhìn spacer ẩn, hoạt động tốt.
paulmelnikow

8

Tôi đã có thể giải quyết điều này hoàn toàn trong IB:

  1. Đặt các ràng buộc để căn chỉnh trung tâm Y của mỗi bài phỏng vấn của bạn với cạnh dưới cùng của giám sát.
  2. Đặt hệ số nhân của từng ràng buộc này thành 1 / 2n, 3 / 2n, 5 / 2n, cảm, n-1 / 2n trong đó n là số lượt xem bạn đang phân phối.

Vì vậy, nếu bạn có ba nhãn, hãy đặt các bội số cho mỗi ràng buộc đó thành 0.1666667, 0.5, 0.833333.


1
Tôi thực sự phải làm 1 / n 3 / n 5 / n 7 / n tối đa (2n-1) / n
Hamzah Malik

5

Tôi tìm thấy một phương pháp hoàn hảo và đơn giản. Bố cục tự động không cho phép bạn thay đổi kích thước các không gian bằng nhau, nhưng nó cho phép bạn thay đổi kích thước các khung nhìn bằng nhau. Chỉ cần đặt một số chế độ xem vô hình ở giữa các trường của bạn và cho biết bố cục tự động để giữ cho chúng có cùng kích thước. Nó hoạt động hoàn hảo!

XIB ban đầu

XIB kéo dài

Một điều cần lưu ý mặc dù; Khi tôi giảm kích thước trong trình thiết kế giao diện, đôi khi nó bị lẫn lộn và để lại một nhãn ở đó và nó có xung đột nếu kích thước bị thay đổi bởi một số tiền lẻ. Nếu không thì nó hoạt động hoàn hảo.

chỉnh sửa: Tôi thấy rằng xung đột đã trở thành một vấn đề. Do đó, tôi đã sử dụng một trong các ràng buộc khoảng cách, xóa nó và thay thế nó bằng hai ràng buộc, lớn hơn hoặc bằng và một ít hơn hoặc bằng. Cả hai đều có cùng kích thước và có mức độ ưu tiên thấp hơn nhiều so với các ràng buộc khác. Kết quả là không có xung đột.


4

Dựa trên câu trả lời của Ben Dolman, điều này phân phối các khung nhìn đồng đều hơn (với phần đệm, v.v.):

+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
                           relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
    NSMutableArray *constraints = [NSMutableArray new];
    NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;

    CGFloat min = 0.25;
    CGFloat max = 1.75;
    CGFloat d = (max-min) / ([views count] - 1);
    for (NSUInteger i = 0; i < [views count]; i++) {
        id view = views[i];
        CGFloat multiplier = i * d + min;
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
                                                                      attribute:attr
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:toView
                                                                      attribute:attr
                                                                     multiplier:multiplier
                                                                       constant:0];
        [constraints addObject:constraint];
    }

    return constraints;
}


3

Với nhãn này hoạt động tốt ít nhất:

@"H:|-15-[first(==second)]-[second(==third)]-[third(==first)]-15-|

Nếu cái thứ nhất có cùng chiều rộng với cái thứ hai và thứ hai thứ ba và thứ ba cái thứ nhất, thì tất cả chúng sẽ có cùng chiều rộng ... Bạn có thể làm cả hai chiều ngang (H) và dọc (V).


3

phiên bản 3 nhanh

let _redView = UIView()
        _redView.backgroundColor = UIColor.red
        _redView.translatesAutoresizingMaskIntoConstraints = false

        let _yellowView = UIView()
        _yellowView.backgroundColor = UIColor.yellow
        _yellowView.translatesAutoresizingMaskIntoConstraints = false

        let _blueView = UIView()
        _blueView.backgroundColor = UIColor.blue
        _blueView.translatesAutoresizingMaskIntoConstraints = false

        self.view.addSubview(_redView)
        self.view.addSubview(_yellowView)
        self.view.addSubview(_blueView)

        var views = ["_redView": _redView, "_yellowView": _yellowView, "_blueView":_blueView]

        var nslayoutConstraint_H = NSLayoutConstraint.constraints(withVisualFormat: "|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views)
        self.view.addConstraints(nslayoutConstraint_H)

        var nslayoutConstraint_V = NSLayoutConstraint.constraints(withVisualFormat: "V:[_redView(60)]", options: NSLayoutFormatOptions.init(rawValue: 0), metrics: nil, views: views)
        self.view.addConstraints(nslayoutConstraint_V)


        let constraint_red = NSLayoutConstraint.init(item: self.view, attribute: .centerY, relatedBy: .equal, toItem: _redView, attribute: .centerY, multiplier: 1, constant: 0)
        self.view.addConstraint(constraint_red)

        let constraint_yellow = NSLayoutConstraint.init(item: self.view, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .centerX, multiplier: 1, constant: 0)
        self.view.addConstraint(constraint_yellow)

        let constraint_yellow1 = NSLayoutConstraint.init(item: _redView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 0.5, constant: 0)
        self.view.addConstraint(constraint_yellow1)

        let constraint_yellow2 = NSLayoutConstraint.init(item: _blueView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 1.5, constant: 40)
        self.view.addConstraint(constraint_yellow2)

1
Xây dựng về cách mã này trả lời các câu hỏi sẽ giúp khách truy cập trong tương lai.
JAL

2

Tôi biết đã được một lúc kể từ câu trả lời đầu tiên, nhưng tôi chỉ gặp phải vấn đề tương tự và tôi muốn chia sẻ giải pháp của mình. Cho các thế hệ sau ...

Tôi đặt quan điểm của mình trên viewDidLoad:

- (void)viewDidLoad {

    [super viewDidLoad];

    cancelButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
    [cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
    [self.view addSubview:cancelButton];

    middleButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    middleButton.translatesAutoresizingMaskIntoConstraints = NO;
    [middleButton setTitle:@"Middle" forState:UIControlStateNormal];
    [self.view addSubview:middleButton];

    nextButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    nextButton.translatesAutoresizingMaskIntoConstraints = NO;
    [nextButton setTitle:@"Next" forState:UIControlStateNormal];
    [self.view addSubview:nextButton];


    [self.view setNeedsUpdateConstraints];

}

Và sau đó, trên updateViewConstrains, đầu tiên tôi xóa tất cả các ràng buộc, sau đó tôi tạo từ điển khung nhìn và sau đó tôi tính không gian được sử dụng giữa các khung nhìn. Sau đó, tôi chỉ sử dụng Định dạng Ngôn ngữ Hình ảnh để đặt các ràng buộc:

- (void)updateViewConstraints {


    [super updateViewConstraints];

    [self.view removeConstraints:self.view.constraints];

    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(cancelButton, nextButton, middleButton);

    float distance=(self.view.bounds.size.width-cancelButton.intrinsicContentSize.width-nextButton.intrinsicContentSize.width-middleButton.intrinsicContentSize.width-20-20)/  ([viewsDictionary count]-1);  // 2 times 20 counts for the left & rigth margins
    NSNumber *distancies=[NSNumber numberWithFloat:distance];

//    NSLog(@"Distancies: %@", distancies);
//    
//    NSLog(@"View Width: %f", self.view.bounds.size.width);
//    NSLog(@"Cancel Width: %f", cancelButton.intrinsicContentSize.width);
//    NSLog(@"Middle Width: %f", middleButton.intrinsicContentSize.width);
//    NSLog(@"Next Width: %f", nextButton.intrinsicContentSize.width);



    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[cancelButton]-dis-[middleButton]-dis-[nextButton]-|"
                                                                   options:NSLayoutFormatAlignAllBaseline
                                                                   metrics:@{@"dis":distancies}
                                                                     views:viewsDictionary];


    [self.view addConstraints:constraints];



    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[nextButton]-|"
                                                          options:0
                                                          metrics:nil
                                                            views:viewsDictionary];
    [self.view addConstraints:constraints];



}

Điểm hay của phương pháp này là bạn phải làm rất ít môn toán. Tôi không nói rằng đây là giải pháp hoàn hảo, nhưng tôi làm việc cho bố cục mà tôi đang cố gắng đạt được.

Tôi hy vọng nó sẽ giúp.


2

Đây là một giải pháp sẽ tập trung theo chiều dọc bất kỳ số lượng các cuộc phỏng vấn, ngay cả khi chúng có kích thước duy nhất. Những gì bạn muốn làm là tạo một container trung cấp, tập trung vào giám sát, sau đó đặt tất cả các cuộc phỏng vấn vào container và sắp xếp chúng với sự tôn trọng lẫn nhau. Nhưng điều quan trọng nhất là bạn cũng cần buộc chúng ở trên cùng dưới cùng của container, vì vậy container có thể có kích thước chính xác và tập trung vào giám sát. Bằng cách tìm ra chiều cao chính xác từ các lần xem xét của nó, container có thể được căn giữa theo chiều dọc.

Trong ví dụ này, selflà giám sát mà bạn đang tập trung vào tất cả các cuộc phỏng vấn.

NSArray *subviews = @[ (your subviews in top-to-bottom order) ];

UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
container.translatesAutoresizingMaskIntoConstraints = NO;
for (UIView *subview in subviews) {
    subview.translatesAutoresizingMaskIntoConstraints = NO;
    [container addSubview:subview];
}
[self addSubview:container];

[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeRight multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]];

if (0 < subviews.count) {
    UIView *firstSubview = subviews[0];
    [container addConstraint:[NSLayoutConstraint constraintWithItem:firstSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                             toItem:container attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]];
    UIView *lastSubview = subviews.lastObject;
    [container addConstraint:[NSLayoutConstraint constraintWithItem:lastSubview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                             toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]];

    UIView *subviewAbove = nil;
    for (UIView *subview in subviews) {
        [container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                                                 toItem:container attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
        if (subviewAbove) {
            [container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                     toItem:subviewAbove attribute:NSLayoutAttributeBottom multiplier:1.0f constant:10.0f]];
        }
        subviewAbove = subview;
    }
}

Đây là giải pháp tốt nhất khi sử dụng các chế độ xem có kích thước khác nhau phải được đặt ở giữa.
noobsmcgoobs

2

Tôi chỉ giải quyết vấn đề của tôi bằng cách sử dụng tính năng số nhân. Tôi không chắc nó hoạt động cho tất cả các trường hợp, nhưng đối với tôi nó hoạt động hoàn hảo. Tôi đang sử dụng Xcode 6.3 FYI.

Điều cuối cùng tôi đã làm là:

1) Trước tiên, các nút của tôi được định vị trên màn hình có chiều rộng 320px được phân phối theo cách tôi muốn nó nhìn trên thiết bị 320px.

Bước 1: định vị các nút

2) Sau đó, tôi đã thêm một ràng buộc Không gian hàng đầu để giám sát trên tất cả các nút của mình.

Bước 2: thêm các hạn chế không gian hàng đầu

3) Sau đó, tôi đã sửa đổi các thuộc tính của không gian hàng đầu sao cho hằng số là 0 và hệ số nhân là độ lệch x chia cho chiều rộng của màn hình (ví dụ: nút đầu tiên của tôi là 8px từ cạnh trái để tôi đặt hệ số nhân của mình thành 8/320)

4) Sau đó, bước quan trọng ở đây là thay đổi Mục thứ hai trong mối quan hệ ràng buộc thành giám sát. Giao dịch thay vì giám sát. Đây là chìa khóa vì giám sát. Đọc là 0 và theo dõi trong trường hợp của tôi là 320, vì vậy 8/320 là 8 px trên thiết bị 320px, sau đó khi chiều rộng của giám sát thay đổi thành 640 hoặc bất cứ điều gì, tất cả các chế độ xem đều di chuyển theo tỷ lệ so với chiều rộng kích thước màn hình 320px. Toán học ở đây đơn giản hơn nhiều để hiểu.

bước 3 & 4: thay đổi hệ số nhân thành xPos / screenWidth và đặt mục thứ hai thành .Trailing


2

Nhiều câu trả lời không đúng, nhưng nhận được nhiều tính. Ở đây tôi chỉ viết một giải pháp theo chương trình, ba khung nhìn được căn ngang, không sử dụng các khung nhìn spacer , nhưng nó chỉ hoạt động khi độ rộng của nhãn được biết khi sử dụng trong bảng phân cảnh .

NSDictionary *views = NSDictionaryOfVariableBindings(_redView, _yellowView, _blueView);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:nil views:views]];

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_redView(60)]" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_redView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:0.5 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_blueView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:1.5 constant:40]];

2

Đây là một câu trả lời khác. Tôi đã trả lời một câu hỏi tương tự và thấy liên kết được tham chiếu đến câu hỏi này. Tôi không thấy bất kỳ câu trả lời tương tự như của tôi. Vì vậy, tôi nghĩ đến việc viết nó ở đây.

  class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.whiteColor()
        setupViews()
    }

    var constraints: [NSLayoutConstraint] = []

    func setupViews() {

        let container1 = createButtonContainer(withButtonTitle: "Button 1")
        let container2 = createButtonContainer(withButtonTitle: "Button 2")
        let container3 = createButtonContainer(withButtonTitle: "Button 3")
        let container4 = createButtonContainer(withButtonTitle: "Button 4")

        view.addSubview(container1)
        view.addSubview(container2)
        view.addSubview(container3)
        view.addSubview(container4)

        [

            // left right alignment
            container1.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
            container1.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20),
            container2.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container2.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
            container3.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container3.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
            container4.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container4.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),


            // place containers one after another vertically
            container1.topAnchor.constraintEqualToAnchor(view.topAnchor),
            container2.topAnchor.constraintEqualToAnchor(container1.bottomAnchor),
            container3.topAnchor.constraintEqualToAnchor(container2.bottomAnchor),
            container4.topAnchor.constraintEqualToAnchor(container3.bottomAnchor),
            container4.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),


            // container height constraints
            container2.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
            container3.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
            container4.heightAnchor.constraintEqualToAnchor(container1.heightAnchor)
            ]
            .forEach { $0.active = true }
    }


    func createButtonContainer(withButtonTitle title: String) -> UIView {
        let view = UIView(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false

        let button = UIButton(type: .System)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle(title, forState: .Normal)
        view.addSubview(button)

        [button.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
            button.leftAnchor.constraintEqualToAnchor(view.leftAnchor),
            button.rightAnchor.constraintEqualToAnchor(view.rightAnchor)].forEach { $0.active = true }

        return view
    }
}

nhập mô tả hình ảnh ở đây

Và một lần nữa, điều này có thể được thực hiện khá dễ dàng với UIStackViews iOS9.

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.greenColor()
        setupViews()
    }

    var constraints: [NSLayoutConstraint] = []

    func setupViews() {

        let container1 = createButtonContainer(withButtonTitle: "Button 1")
        let container2 = createButtonContainer(withButtonTitle: "Button 2")
        let container3 = createButtonContainer(withButtonTitle: "Button 3")
        let container4 = createButtonContainer(withButtonTitle: "Button 4")

        let stackView = UIStackView(arrangedSubviews: [container1, container2, container3, container4])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .Vertical
        stackView.distribution = .FillEqually
        view.addSubview(stackView)

        [stackView.topAnchor.constraintEqualToAnchor(view.topAnchor),
            stackView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),
            stackView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
            stackView.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20)].forEach { $0.active = true }
    }


    func createButtonContainer(withButtonTitle title: String) -> UIView {
        let button = UIButton(type: .Custom)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor.redColor()
        button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        button.setTitle(title, forState: .Normal)
        let buttonContainer = UIStackView(arrangedSubviews: [button])
        buttonContainer.distribution = .EqualCentering
        buttonContainer.alignment = .Center
        buttonContainer.translatesAutoresizingMaskIntoConstraints = false
        return buttonContainer
    }
}

Lưu ý rằng đó là cách tiếp cận chính xác như trên. Nó thêm bốn khung nhìn container được lấp đầy bằng nhau và một khung nhìn được thêm vào mỗi khung nhìn ngăn xếp được căn chỉnh ở giữa. Nhưng, phiên bản UIStackView này giảm một số mã và trông đẹp mắt.


1

Một cách tiếp cận khác có thể là các nhãn trên cùng và dưới cùng có các ràng buộc tương ứng với chế độ xem trên cùng và dưới cùng, và có chế độ xem giữa có các ràng buộc trên và dưới tương ứng so với chế độ xem thứ nhất và thứ ba, tương ứng.

Lưu ý rằng bạn có nhiều quyền kiểm soát hơn các ràng buộc hơn có thể bằng cách kéo các khung nhìn gần nhau cho đến khi các đường đứt nét hướng dẫn xuất hiện - những điều này cho thấy các ràng buộc giữa hai đối tượng sẽ được hình thành thay vì giữa đối tượng và giám sát.

Trong trường hợp này, sau đó bạn muốn thay đổi các ràng buộc thành "Lớn hơn hoặc bằng" giá trị mong muốn, thay vì "bằng" để cho phép chúng thay đổi kích thước. Không chắc chắn nếu điều này sẽ làm chính xác những gì bạn muốn.


1

Cách rất dễ dàng để giải quyết điều này trong InterfaceBuilder:

Đặt nhãn ở giữa (nhãn2) thành "Trung tâm ngang trong vùng chứa" và "Trung tâm dọc trong vùng chứa"

Chọn nhãn ở giữa và nhãn trên cùng (nhãn1 + nhãn2) và thêm các ràng buộc TWO cho Khoảng cách dọc. Một với khoảng cách lớn hơn hoặc bằng khoảng cách tối thiểu. Một với ít hơn hoặc bằng khoảng cách tối đa.

Tương tự cho nhãn trung tâm và nhãn dưới cùng (nhãn2 + nhãn3).

Ngoài ra, bạn cũng có thể thêm hai ràng buộc cho nhãn1 - Không gian hàng đầu cho SuperView và hai ràng buộc cho nhãn2 - Không gian dưới cùng cho SuperView.

Kết quả sẽ là tất cả 4 khoảng cách sẽ thay đổi kích thước bằng nhau.


2
Tôi thấy rằng điều này chỉ hoạt động khi bạn thay đổi kích thước giám sát để khoảng cách lấy chính xác các giá trị tối thiểu hoặc tối đa. Khi bạn thay đổi kích thước của nó thành một giá trị ở đâu đó giữa, các cuộc phỏng vấn không được phân phối đồng đều.
Dorian Roy

1

Tôi đã thực hiện một chức năng có thể giúp đỡ. Ví dụ sử dụng này:

 [self.view addConstraints: [NSLayoutConstraint fluidConstraintWithItems:NSDictionaryOfVariableBindings(button1, button2, button3)
                                                                asString:@[@"button1", @"button2", @"button3"]
                                                               alignAxis:@"V"
                                                          verticalMargin:100
                                                        horizontalMargin:50
                                                             innerMargin:25]];

sẽ gây ra sự phân phối dọc (xin lỗi không có 10 danh tiếng để nhúng hình ảnh). Và nếu bạn thay đổi trục và một số giá trị lề:

alignAxis:@"H"
verticalMargin:120
horizontalMargin:20
innerMargin:10

Bạn sẽ nhận được phân phối ngang đó .

Tôi là người mới chơi iOS nhưng voilà!

EvenDistribution.h

@interface NSLayoutConstraint (EvenDistribution)

/**
 * Returns constraints that will cause a set of subviews
 * to be evenly distributed along an axis.
 */
+ (NSArray *)  fluidConstraintWithItems:(NSDictionary *) views
                               asString:(NSArray *) stringViews
                              alignAxis:(NSString *) axis
                         verticalMargin:(NSUInteger) vMargin
                       horizontalMargin:(NSUInteger) hMargin
                            innerMargin:(NSUInteger) inner;
@end

EvenDistribution.m

#import "EvenDistribution.h"

@implementation NSLayoutConstraint (EvenDistribution)

+ (NSArray *) fluidConstraintWithItems:(NSDictionary *) dictViews
                              asString:(NSArray *) stringViews
                             alignAxis:(NSString *) axis
                        verticalMargin:(NSUInteger) vMargin
                      horizontalMargin:(NSUInteger) hMargin
                           innerMargin:(NSUInteger) iMargin

{
    NSMutableArray *constraints = [NSMutableArray arrayWithCapacity: dictViews.count];
    NSMutableString *globalFormat = [NSMutableString stringWithFormat:@"%@:|-%d-",
                                     axis,
                                     [axis isEqualToString:@"V"] ? vMargin : hMargin
                                     ];



        for (NSUInteger i = 0; i < dictViews.count; i++) {

            if (i == 0)
                [globalFormat appendString:[NSString stringWithFormat: @"[%@]-%d-", stringViews[i], iMargin]];
            else if(i == dictViews.count - 1)
                [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-", stringViews[i], stringViews[i-1]]];
            else
               [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-%d-", stringViews[i], stringViews[i-1], iMargin]];

            NSString *localFormat = [NSString stringWithFormat: @"%@:|-%d-[%@]-%d-|",
                                     [axis isEqualToString:@"V"] ? @"H" : @"V",
                                     [axis isEqualToString:@"V"] ? hMargin : vMargin,
                                     stringViews[i],
                                     [axis isEqualToString:@"V"] ? hMargin : vMargin];

            [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:localFormat
                                                                                     options:0
                                                                                     metrics:nil
                                                                                       views:dictViews]];


    }
    [globalFormat appendString:[NSString stringWithFormat:@"%d-|",
                                [axis isEqualToString:@"V"] ? vMargin : hMargin
                                ]];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:globalFormat
                                                                             options:0
                                                                             metrics:nil
                                                                               views:dictViews]];

    return constraints;

}

@end

1

Có, bạn chỉ có thể thực hiện việc này trong trình tạo giao diện và không cần viết mã - một điều lưu ý là bạn đang thay đổi kích thước nhãn thay vì phân phối khoảng trắng. Trong trường hợp này, căn chỉnh Nhãn X và Y của Nhãn 2 cho giám sát để nó được cố định ở giữa. Sau đó, đặt không gian dọc của nhãn 1 thành giám sát và để nhãn 2 thành tiêu chuẩn, lặp lại cho nhãn 3. Sau khi đặt nhãn 2, cách dễ nhất để đặt nhãn 1 và 3 là thay đổi kích thước cho đến khi chúng chụp.

Dưới đây là màn hình ngang, lưu ý rằng không gian dọc giữa nhãn 1 và 2 được đặt thành tiêu chuẩn:màn hình ngang

Và đây là phiên bản chân dung:nhập mô tả hình ảnh ở đây

Tôi nhận ra rằng chúng không hoàn toàn cách nhau 100% giữa các đường cơ sở do sự khác biệt giữa không gian tiêu chuẩn giữa các nhãn và không gian tiêu chuẩn đối với giám sát. Nếu điều đó làm phiền bạn, hãy đặt kích thước thành 0 thay vì tiêu chuẩn


1

Tôi đặt giá trị chiều rộng chỉ cho mục đầu tiên (> = chiều rộng) và khoảng cách tối thiểu giữa mỗi mục (> = khoảng cách). Sau đó, tôi sử dụng Ctrl để kéo mục thứ hai, thứ ba ... trên mục đầu tiên để xâu chuỗi các phụ thuộc giữa các mục.

nhập mô tả hình ảnh ở đây


1

Đi dự tiệc muộn nhưng tôi có một giải pháp làm việc để tạo một menu theo chiều ngang với khoảng cách. Nó có thể được thực hiện dễ dàng bằng ==NSLayoutConstraint

const float MENU_HEIGHT = 40;

- (UIView*) createMenuWithLabels: (NSArray *) labels
    // labels is NSArray of NSString
    UIView * backgroundView = [[UIView alloc]init];
    backgroundView.translatesAutoresizingMaskIntoConstraints = false;

    NSMutableDictionary * views = [[NSMutableDictionary alloc] init];
    NSMutableString * format = [[NSMutableString alloc] initWithString: @"H:|"];
    NSString * firstLabelKey;

    for(NSString * str in labels)
    {
        UILabel * label = [[UILabel alloc] init];
        label.translatesAutoresizingMaskIntoConstraints = false;
        label.text = str;
        label.textAlignment = NSTextAlignmentCenter;
        label.textColor = [UIColor whiteColor];
        [backgroundView addSubview: label];
        [label fixHeightToTopBounds: MENU_HEIGHT-2];
        [backgroundView addConstraints: [label fixHeightToTopBounds: MENU_HEIGHT]];
        NSString * key = [self camelCaseFromString: str];
        [views setObject: label forKey: key];
        if(firstLabelKey == nil)
        {
            [format appendString: [NSString stringWithFormat: @"[%@]", key]];
            firstLabelKey = key;
        }
        else
        {
            [format appendString: [NSString stringWithFormat: @"[%@(==%@)]", key, firstLabelKey]];
        }
    }

    [format appendString: @"|"];

    NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat: (NSString *) format
                                                                               options: 0
                                                                               metrics: nil
                                                                                 views: (NSDictionary *) views];
    [backgroundView addConstraints: constraints];
    return backgroundView;
}

0

Android có một phương pháp kết nối các khung nhìn với nhau trong hệ thống bố cục dựa trên ràng buộc mà tôi muốn bắt chước. Tìm kiếm đưa tôi đến đây nhưng không có câu trả lời nào khá hiệu quả. Tôi không muốn sử dụng StackViews vì chúng có xu hướng khiến tôi đau buồn hơn là họ tiết kiệm trước. Cuối cùng tôi đã tạo ra một giải pháp sử dụng UILayoutGuides được đặt giữa các khung nhìn. Kiểm soát độ rộng của chúng cho phép các loại phân phối khác nhau, kiểu chuỗi theo cách nói của Android. Hàm chấp nhận một neo hàng đầu và dấu thay vì một khung nhìn cha. Điều này cho phép chuỗi được đặt giữa hai khung nhìn tùy ý thay vì phân phối bên trong khung nhìn cha. Nó không sử dụng UILayoutGuidechỉ có trong iOS 9+ nhưng điều đó không còn là vấn đề nữa.

public enum LayoutConstraintChainStyle {
    case spread //Evenly distribute between the anchors
    case spreadInside //Pin the first & last views to the sides and then evenly distribute
    case packed //The views have a set space but are centered between the anchors.
}

public extension NSLayoutConstraint {

    static func chainHorizontally(views: [UIView],
                                  leadingAnchor: NSLayoutXAxisAnchor,
                                  trailingAnchor: NSLayoutXAxisAnchor,
                                  spacing: CGFloat = 0.0,
                                  style: LayoutConstraintChainStyle = .spread) -> [NSLayoutConstraint] {
    var constraints = [NSLayoutConstraint]()
    guard views.count > 1 else { return constraints }
    guard let first = views.first, let last = views.last, let superview = first.superview else { return constraints }

    //Setup the chain of views
    var distributionGuides = [UILayoutGuide]()
    var previous = first
    let firstGuide = UILayoutGuide()
    superview.addLayoutGuide(firstGuide)
    distributionGuides.append(firstGuide)
    firstGuide.identifier = "ChainDistribution\(distributionGuides.count)"
    constraints.append(firstGuide.leadingAnchor.constraint(equalTo: leadingAnchor))
    constraints.append(first.leadingAnchor.constraint(equalTo: firstGuide.trailingAnchor, constant: spacing))
    views.dropFirst().forEach { view in
        let g = UILayoutGuide()
        superview.addLayoutGuide(g)
        distributionGuides.append(g)
        g.identifier = "ChainDistribution\(distributionGuides.count)"
        constraints.append(contentsOf: [
            g.leadingAnchor.constraint(equalTo: previous.trailingAnchor),
            view.leadingAnchor.constraint(equalTo: g.trailingAnchor)
        ])
        previous = view
    }
    let lastGuide = UILayoutGuide()
    superview.addLayoutGuide(lastGuide)
    constraints.append(contentsOf: [lastGuide.leadingAnchor.constraint(equalTo: last.trailingAnchor),
                                    lastGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
    distributionGuides.append(lastGuide)

    //Space the according to the style.
    switch style {
    case .packed:
        if let first = distributionGuides.first, let last = distributionGuides.last {
            constraints.append(first.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
            constraints.append(last.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
            constraints.append(last.widthAnchor.constraint(equalTo: first.widthAnchor))
            constraints.append(contentsOf:
                distributionGuides.dropFirst().dropLast()
                    .map { $0.widthAnchor.constraint(equalToConstant: spacing) }
                )
        }
    case .spread:
        if let first = distributionGuides.first {
            constraints.append(contentsOf:
                distributionGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: first.widthAnchor) })
        }
    case .spreadInside:
        if let first = distributionGuides.first, let last = distributionGuides.last {
            constraints.append(first.widthAnchor.constraint(equalToConstant: spacing))
            constraints.append(last.widthAnchor.constraint(equalToConstant: spacing))
            let innerGuides = distributionGuides.dropFirst().dropLast()
            if let key = innerGuides.first {
                constraints.append(contentsOf:
                    innerGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: key.widthAnchor) }
                )
            }
        }
    }

    return constraints
}

0

Tôi muốn căn chỉnh theo chiều ngang 5 hình ảnh, vì vậy tôi đã kết thúc theo phản hồi của Mete với một sự khác biệt nhỏ.

Hình ảnh đầu tiên sẽ được căn giữa theo chiều ngang trong container bằng 0 và hệ số nhân là 1: 5:

hình ảnh đầu tiên

Hình ảnh thứ hai sẽ được căn giữa theo chiều ngang trong container bằng 0 và hệ số nhân là 3: 5: hình ảnh thứ hai

Và như thế cho phần còn lại của hình ảnh. Ví dụ: hình ảnh thứ năm (và cuối cùng) sẽ được căn giữa theo chiều ngang trong vùng chứa bằng 0 và hệ số nhân là 9: 5: hình ảnh cuối cùng

Như Mete đã giải thích, thứ tự đi 1, 3, 5, 7, 9, v.v ... Các vị trí theo cùng một logic: vị trí đầu tiên là 1, sau đó là không gian, sau đó là vị trí tiếp theo 3, v.v.


-1

Tại sao bạn không chỉ tạo một bảng Xem và thực hiện isScrollEnabled = false


Không hiểu downvote, giải pháp của tôi là suy nghĩ bên ngoài hộp. Tại sao phải trải qua việc tạo danh sách với tính năng tự động thanh toán khi ca cao cung cấp cho bạn một danh sách trong UITableView?
Josh O'Connor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.