Додайте перегляди в UIStackView програмно


158

Я намагаюся додавати перегляди в UIStackView програмно. Наразі Мій код:

UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 100, 100)];

UIView *view2 =  [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];

[self.stack1 addArrangedSubview:view1];
[self.stack1 addArrangedSubview:view2];

Коли я розгортаю додаток, є лише 1 вид і він із чорним кольором. (View1 також отримує параметри для view2)


Ви здорово перевірили вашу розетку? Ви реєстрували підперегляди під час виконання?
Вени

10
Використовуйте addArrangedSubview:, а неaddSubview:
pkamb

Відповіді:


245

Перегляди стека використовують внутрішній розмір вмісту, тому використовуйте обмеження для компонування для визначення розмірів представлень.

Існує простий спосіб швидко додати обмеження (приклад):

[view1.heightAnchor constraintEqualToConstant:100].active = true;

Повний код:

- (void) setup {

    //View 1
    UIView *view1 = [[UIView alloc] init];
    view1.backgroundColor = [UIColor blueColor];
    [view1.heightAnchor constraintEqualToConstant:100].active = true;
    [view1.widthAnchor constraintEqualToConstant:120].active = true;


    //View 2
    UIView *view2 = [[UIView alloc] init];
    view2.backgroundColor = [UIColor greenColor];
    [view2.heightAnchor constraintEqualToConstant:100].active = true;
    [view2.widthAnchor constraintEqualToConstant:70].active = true;

    //View 3
    UIView *view3 = [[UIView alloc] init];
    view3.backgroundColor = [UIColor magentaColor];
    [view3.heightAnchor constraintEqualToConstant:100].active = true;
    [view3.widthAnchor constraintEqualToConstant:180].active = true;

    //Stack View
    UIStackView *stackView = [[UIStackView alloc] init];

    stackView.axis = UILayoutConstraintAxisVertical;
    stackView.distribution = UIStackViewDistributionEqualSpacing;
    stackView.alignment = UIStackViewAlignmentCenter;
    stackView.spacing = 30;


    [stackView addArrangedSubview:view1];
    [stackView addArrangedSubview:view2];
    [stackView addArrangedSubview:view3];

    stackView.translatesAutoresizingMaskIntoConstraints = false;
    [self.view addSubview:stackView];


    //Layout for Stack View
    [stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = true;
    [stackView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = true;
}

Примітка. Це було протестовано на iOS 9

Рівний пробіл UIStackView (по центру)


26
Сенс використання представлень стека полягає в тому, що вони приховують деталі управління обмеженнями від розробника. Як ви зазначали, це покладається на підперегляди, що мають внутрішній розмір вмісту, якого за замовчуванням UIViewsне роблять. Якби оригінальний плакат використовував UILabelекземпляри замість UIView, його код працював би так, як він очікував. Я додав приклад, який демонструє це нижче.
Грег Браун

коли я це роблю "[view1.heightAnchor constraintEqualToConstant: 100] .active = true;" я отримую помилку на ios 8. Він працює лише на ios 9.Я знаю, що uistackview призначений для ios 9+, але як я можу встановити обмеження висоти Ancor для ios 8
nuridin selimko

Мій StackView додається за допомогою раскадровки. Під час виконання я додаю декілька UIView (містять інші підгляди) до stackView. Після завантаження даних усі перегляди всередині stackView мають однакову висоту, вони не змінюють розмір відповідно до вмісту. @ user1046037
Mansuu ....

Чи встановлені обмеження для цих окремих поглядів, щоб їх розмір мінявся залежно від вмісту?
користувач1046037

Це найкращий та оптимальний спосіб керування видами з однаковою шириною чи висотою
Кампай

156

Швидкий 5.0

//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")

//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text  = "Hi World"
textLabel.textAlignment = .center

//Stack View
let stackView   = UIStackView()
stackView.axis  = NSLayoutConstraint.Axis.vertical
stackView.distribution  = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing   = 16.0

stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false

self.view.addSubview(stackView)

//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

На основі відповіді @ user1046037


2
Запускаючи iOS 8 , можна активувати обмеження в пакетній роботі за допомогою activate(_:), і, як правило, ефективніше це зробити таким чином developer.apple.com/documentation/uikit/nslayoutconstraint/…
Джонатан Кабрера


17

UIStackViewвикористовує внутрішньо обмеження для позиціонування влаштованих підпереглядів. Саме те, які обмеження створюються, залежить від того, як налаштовано сам вид стека. За замовчуванням подання стека створюватиме обмеження, які викладають його підпорядковані підгляди по горизонтальній лінії, закріплюючи передові та кінцеві подання до власних передових і кінцевих країв. Отже, ваш код створить макет, який виглядає приблизно так:

|[view1][view2]|

Простір, який виділяється для кожного підперегляду, визначається низкою факторів, включаючи внутрішній розмір субпрогляду та його стійкість до стиснення та пріоритети захоплення вмісту. За замовчуванням UIViewекземпляри не визначають внутрішній розмір вмісту. Це щось, що зазвичай надається підкласом, таким як UILabelабоUIButton .

Оскільки опір стиснення вмісту та пріоритети захоплення вмісту двох нових UIViewекземплярів будуть однаковими, і жоден з представлень не забезпечує внутрішнього розміру вмісту, механізм компонування повинен найкращим чином здогадуватися, який розмір повинен бути призначений для кожного перегляду. У вашому випадку він призначає першому виду 100% наявного простору, а другому - нічого.

Якщо ви модифікуєте свій код, щоб UILabelнатомість використовувати екземпляри, ви отримаєте кращі результати:

UILabel *label1 = [UILabel new];
label1.text = @"Label 1";
label1.backgroundColor = [UIColor blueColor];

UILabel *label2 = [UILabel new];
label2.text = @"Label 2";
label2.backgroundColor = [UIColor greenColor];

[self.stack1 addArrangedSubview:label1];
[self.stack1 addArrangedSubview:label2];

Зауважте, що не потрібно чітко створювати будь-які обмеження самостійно. Це головна перевага використання UIStackView- вона приховує (часто некрасиві) деталі управління обмеженнями від розробника.


У мене така ж проблема, що це працює для міток, але не для TextFields (або переглядів, з цього приводу). Також у всіх навчальних посібниках, які я знаходжу, використовуються мітки, зображення чи кнопки. Чи означає це, що для чогось іншого, ніж ці елементи інтерфейсу, ми не можемо використовувати цей метод?
physicalattraction

@physicalattraction Перегляди, які ви додаєте до подання стека, повинні мати внутрішній розмір вмісту. Наскільки я пам’ятаю, внутрішній розмір текстового поля заснований на вмісті поля (який непарний). Самі екземпляри UIViewне мають внутрішнього розміру. Можливо, вам доведеться застосувати додаткові обмеження до цих представлень, щоб представлення стека знало, наскільки вони великі.
Грег Браун

Має сенс, тому я це намагаюся. Тепер я побудував перегляд у xib з текстовим полем, якому я даю явне обмеження висоти в 30 пікселів. Крім того, я роблю такі два обмеження: MyTextField.top = top+20і bottom = MyTextField.bottom+20. Я б очікував, що це дасть моєму погляду внутрішній зріст 70, але натомість він скаржиться на суперечливі обмеження. Ви знаєте, що тут відбувається? Саме цей погляд я хочу розмістити всередині свого UIStackView.
physicalattraction

Я думаю, я знаю, в чому проблема. Перегляд стека автоматично прив'язує остаточний розміщений підперегляд до нижнього краю. Тож подання стека намагається зробити перегляд контейнера такого ж розміру, як і сам. Однак ви сказали цю точку зору, що вона повинна бути висотою 70 пікселів, що створює конфлікт. Спробуйте створити додатковий порожній вигляд одразу після перегляду контейнера та надайте йому вертикальний пріоритет зміщення вмісту 0. Надайте перегляду контейнера вертикальний вміст, який має пріоритет 1000, що призведе до розтягування порожнього простору, щоб заповнити порожній простір у стеку вид.
Грег Браун

Я припускаю, що ви, до речі, використовуєте подання вертикального стека. Ви також можете перевірити цю статтю, про яку я писав про перегляд стеків: dzone.com/articles/…
Грег Браун

13

У Swift 4.2

let redView = UIView()
    redView.backgroundColor = .red

    let blueView = UIView()
    blueView.backgroundColor = .blue

    let stackView = UIStackView(arrangedSubviews: [redView, blueView])
    stackView.axis = .vertical
    stackView.distribution = .fillEqually

    view.addSubview(stackView)

    //        stackView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)

    // autolayout constraint
    stackView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([stackView.topAnchor.constraint(equalTo: view.topAnchor), stackView.leftAnchor.constraint(equalTo: view.leftAnchor), stackView.rightAnchor.constraint(equalTo: view.rightAnchor), stackView.heightAnchor.constraint(equalToConstant: 200)])

8

Ви повинні встановити тип розподілу. У своєму коді juste додати:

self.stack1.distribution = UIStackViewDistributionFillEqually;

Або ви можете встановити розподіл безпосередньо у своєму конструкторі інтерфейсів. Наприклад:

введіть тут опис зображення

Сподіваюся, що це допомагає;) Лапіну.


8

Наступні два рядки вирішили мою проблему

    view.heightAnchor.constraintEqualToConstant(50).active = true;
    view.widthAnchor.constraintEqualToConstant(350).active = true;

Швидка версія -

    var DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
    DynamicView.backgroundColor=UIColor.greenColor()
    DynamicView.layer.cornerRadius=25
    DynamicView.layer.borderWidth=2
    self.view.addSubview(DynamicView)
    DynamicView.heightAnchor.constraintEqualToConstant(50).active = true;
    DynamicView.widthAnchor.constraintEqualToConstant(350).active = true;

    var DynamicView2=UIView(frame: CGRectMake(100, 310, 100, 100))
    DynamicView2.backgroundColor=UIColor.greenColor()
    DynamicView2.layer.cornerRadius=25
    DynamicView2.layer.borderWidth=2
    self.view.addSubview(DynamicView2)
    DynamicView2.heightAnchor.constraintEqualToConstant(50).active = true;
    DynamicView2.widthAnchor.constraintEqualToConstant(350).active = true;

    var DynamicView3:UIView=UIView(frame: CGRectMake(10, 420, 355, 100))
    DynamicView3.backgroundColor=UIColor.greenColor()
    DynamicView3.layer.cornerRadius=25
    DynamicView3.layer.borderWidth=2
    self.view.addSubview(DynamicView3)

    let yourLabel:UILabel = UILabel(frame: CGRectMake(110, 10, 200, 20))
    yourLabel.textColor = UIColor.whiteColor()
    //yourLabel.backgroundColor = UIColor.blackColor()
    yourLabel.text = "mylabel text"
    DynamicView3.addSubview(yourLabel)
    DynamicView3.heightAnchor.constraintEqualToConstant(50).active = true;
    DynamicView3.widthAnchor.constraintEqualToConstant(350).active = true;

    let stackView   = UIStackView()
    stackView.axis  = UILayoutConstraintAxis.Vertical
    stackView.distribution  = UIStackViewDistribution.EqualSpacing
    stackView.alignment = UIStackViewAlignment.Center
    stackView.spacing   = 30

    stackView.addArrangedSubview(DynamicView)
    stackView.addArrangedSubview(DynamicView2)
    stackView.addArrangedSubview(DynamicView3)

    stackView.translatesAutoresizingMaskIntoConstraints = false;

    self.view.addSubview(stackView)

    //Constraints
    stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
    stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true

8

Для прийнятої відповіді, коли ви намагаєтесь приховати будь-який вид всередині подання стека, обмеження працює неправильно.

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x618000086e50 UIView:0x7fc11c4051c0.height == 120   (active)>",
    "<NSLayoutConstraint:0x610000084fb0 'UISV-hiding' UIView:0x7fc11c4051c0.height == 0   (active)>"
)

Причина полягає в тому, що якщо заховати viewв stackViewньому, буде встановлено висоту на 0, щоб оживити його.

Рішення змінити обмеження, priorityяк показано нижче.

import UIKit

class ViewController: UIViewController {

    let stackView = UIStackView()
    let a = UIView()
    let b = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        a.backgroundColor = UIColor.red
        a.widthAnchor.constraint(equalToConstant: 200).isActive = true
        let aHeight = a.heightAnchor.constraint(equalToConstant: 120)
        aHeight.isActive = true
        aHeight.priority = 999

        let bHeight = b.heightAnchor.constraint(equalToConstant: 120)
        bHeight.isActive = true
        bHeight.priority = 999
        b.backgroundColor = UIColor.green
        b.widthAnchor.constraint(equalToConstant: 200).isActive = true

        view.addSubview(stackView)
        stackView.backgroundColor = UIColor.blue
        stackView.addArrangedSubview(a)
        stackView.addArrangedSubview(b)
        stackView.axis = .vertical
        stackView.distribution = .equalSpacing
        stackView.translatesAutoresizingMaskIntoConstraints = false
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // Just add a button in xib file or storyboard and add connect this action.
    @IBAction func test(_ sender: Any) {
        a.isHidden = !a.isHidden
    }

}

5
    //Image View
    let imageView               = UIImageView()
    imageView.backgroundColor   = UIColor.blueColor()
    imageView.heightAnchor.constraintEqualToConstant(120.0).active = true
    imageView.widthAnchor.constraintEqualToConstant(120.0).active = true
    imageView.image = UIImage(named: "buttonFollowCheckGreen")

    //Text Label
    let textLabel               = UILabel()
    textLabel.backgroundColor   = UIColor.greenColor()
    textLabel.widthAnchor.constraintEqualToConstant(self.view.frame.width).active = true
    textLabel.heightAnchor.constraintEqualToConstant(20.0).active = true
    textLabel.text  = "Hi World"
    textLabel.textAlignment = .Center


    //Third View
    let thirdView               = UIImageView()
    thirdView.backgroundColor   = UIColor.magentaColor()
    thirdView.heightAnchor.constraintEqualToConstant(120.0).active = true
    thirdView.widthAnchor.constraintEqualToConstant(120.0).active = true
    thirdView.image = UIImage(named: "buttonFollowMagenta")


    //Stack View
    let stackView   = UIStackView()
    stackView.axis  = UILayoutConstraintAxis.Vertical
    stackView.distribution  = UIStackViewDistribution.EqualSpacing
    stackView.alignment = UIStackViewAlignment.Center
    stackView.spacing   = 16.0

    stackView.addArrangedSubview(imageView)
    stackView.addArrangedSubview(textLabel)
    stackView.addArrangedSubview(thirdView)
    stackView.translatesAutoresizingMaskIntoConstraints = false;

    self.view.addSubview(stackView)

    //Constraints
    stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
    stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true

Удосконалено у відповідь @ Олег Попов


4

У моєму випадку, з чим я морочився, я б очікував, що мені не вистачає цієї лінії:

stackView.translatesAutoresizingMaskIntoConstraints = false;

Після цього немає необхідності встановлювати обмеження для моїх упорядкованих підглядів, але стек-огляд бере на себе це.


4

Swift 5 версія відповіді Олега Попова , яка ґрунтується на відповіді користувача1046037

//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")

//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text  = "Hi World"
textLabel.textAlignment = .center

//Stack View
let stackView   = UIStackView()
stackView.axis  = NSLayoutConstraint.Axis.vertical
stackView.distribution  = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing   = 16.0

stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false

self.view.addSubview(stackView)

//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

1

Я просто зіткнувся з дуже подібною проблемою. Так само, як згадувалося перед розмірами подання стека, залежить один внутрішній розмір вмісту впорядкованого підвиду. Ось моє рішення у Swift 2.x та наступна структура перегляду:

вид - UIView

customView - CustomView: UIView

stackView - UISTackView

упорядковані підгляди - спеціальні підкласи UIView

    //: [Previous](@previous)

import Foundation
import UIKit
import XCPlayground

/**Container for stack view*/
class CustomView:UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }


    init(){
        super.init(frame: CGRectZero)

    }

}

/**Custom Subclass*/
class CustomDrawing:UIView{
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()

    }

    func setup(){
       // self.backgroundColor = UIColor.clearColor()
        print("setup \(frame)")
    }

    override func drawRect(rect: CGRect) {
        let ctx = UIGraphicsGetCurrentContext()
        CGContextMoveToPoint(ctx, 0, 0)
        CGContextAddLineToPoint(ctx, CGRectGetWidth(bounds), CGRectGetHeight(bounds))
        CGContextStrokePath(ctx)

        print("DrawRect")

    }
}



//: [Next](@next)
let stackView = UIStackView()
stackView.distribution = .FillProportionally
stackView.alignment = .Center
stackView.axis = .Horizontal
stackView.spacing = 10


//container view
let view = UIView(frame: CGRectMake(0,0,320,640))
view.backgroundColor = UIColor.darkGrayColor()
//now custom view

let customView = CustomView()

view.addSubview(customView)

customView.translatesAutoresizingMaskIntoConstraints = false
customView.widthAnchor.constraintEqualToConstant(220).active = true
customView.heightAnchor.constraintEqualToConstant(60).active = true
customView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
customView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
customView.backgroundColor = UIColor.lightGrayColor()

//add a stack view
customView.addSubview(stackView)
stackView.centerXAnchor.constraintEqualToAnchor(customView.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(customView.centerYAnchor).active = true
stackView.translatesAutoresizingMaskIntoConstraints = false


let c1 = CustomDrawing()
c1.translatesAutoresizingMaskIntoConstraints = false
c1.backgroundColor = UIColor.redColor()
c1.widthAnchor.constraintEqualToConstant(30).active = true
c1.heightAnchor.constraintEqualToConstant(30).active = true

let c2 = CustomDrawing()
c2.translatesAutoresizingMaskIntoConstraints = false
c2.backgroundColor = UIColor.blueColor()
c2.widthAnchor.constraintEqualToConstant(30).active = true
c2.heightAnchor.constraintEqualToConstant(30).active = true


stackView.addArrangedSubview(c1)
stackView.addArrangedSubview(c2)


XCPlaygroundPage.currentPage.liveView = view

-2

Спробуйте нижче код,

UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 50, 50)];

UIView *view2 =  [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];

NSArray *subView = [NSArray arrayWithObjects:view1,view2, nil];

[self.stack1 initWithArrangedSubviews:subView];

Сподіваюся, це спрацює. Будь ласка, дайте мені знати, якщо вам потрібно більше роз'яснень.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.