Tambahkan tampilan di UIStackView secara terprogram

158

Saya mencoba menambahkan tampilan di UIStackView secara terprogram. Untuk saat ini kode saya adalah:

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

Ketika saya menggunakan aplikasi, hanya ada 1 tampilan dan itu adalah dengan warna hitam. (View1 mendapatkan parameter untuk view2 juga)

Altimir Antonov
sumber
Anda kewarasan memeriksa outlet Anda? Apakah Anda mencatat subview saat runtime?
Wain
10
Gunakan addArrangedSubview:, bukanaddSubview:
pkamb

Jawaban:

245

Tampilan tumpukan menggunakan ukuran konten intrinsik, jadi gunakan batasan tata letak untuk menentukan dimensi tampilan.

Ada cara mudah untuk menambahkan kendala dengan cepat (contoh):

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

Kode Lengkap:

- (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;
}

Catatan: Ini diuji pada iOS 9

UIStackView Equal Spacing (tengah)

pengguna1046037
sumber
26
Inti dari menggunakan tampilan stack adalah mereka menyembunyikan detail manajemen kendala dari pengembang. Seperti yang Anda tunjukkan, ini bergantung pada subview yang memiliki ukuran konten intrinsik, yang standarnya UIViewstidak. Jika poster asli menggunakan UILabelinstance UIView, kode-nya tidak akan berfungsi seperti yang diharapkan. Saya menambahkan contoh yang menunjukkan ini di bawah.
Greg Brown
ketika saya melakukan ini "[view1.heightAnchor constraintEqualToConstant: 100] .active = true;" Saya mendapatkan kesalahan pada ios 8.Ini hanya bekerja pada ios 9. Saya tahu bahwa uistackview adalah untuk ios 9+ tetapi bagaimana saya bisa mengatur batasan heightAncor untuk ios 8
nuridin selimko
StackView saya ditambahkan menggunakan storyboard. Saat runtime saya menambahkan beberapa UIViews (berisi subview lainnya) ke stackView. Setelah memuat data semua tampilan di dalam stackView memiliki ketinggian yang sama, mereka tidak diubah ukurannya sesuai dengan konten. @ user1046037
Mansuu ....
Apakah Anda memiliki batasan yang ditetapkan untuk masing-masing tampilan sehingga ukurannya akan bervariasi berdasarkan konten?
user1046037
Ini adalah cara terbaik & optimal untuk mengelola tampilan dengan lebar atau ketinggian yang sama
Kampai
156

Swift 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

Berdasarkan jawaban @ user1046037.

Oleg Popov
sumber
2
Mulai iOS 8 , dimungkinkan untuk mengaktifkan kendala dalam penggunaan batch activate(_:), dan biasanya lebih efisien untuk melakukannya dengan cara ini developer.apple.com/documentation/uikit/nslayoutconstraint/…
Jonathan Cabrera
Versi Swift 5: stackoverflow.com/a/58827722/2273338
Abdurrahman Mubeen Ali
17

UIStackViewmenggunakan kendala secara internal untuk mengatur subview yang diatur. Kendala apa yang dibuat tergantung pada bagaimana tampilan tumpukan itu sendiri dikonfigurasi. Secara default, tampilan tumpukan akan menciptakan kendala yang mengatur tampilan subviews dalam garis horizontal, menyematkan tampilan depan dan belakang ke tepi depan dan belakangnya sendiri. Jadi kode Anda akan menghasilkan tata letak yang terlihat seperti ini:

|[view1][view2]|

Ruang yang dialokasikan untuk masing-masing subview ditentukan oleh sejumlah faktor termasuk ukuran konten intrinsik subview dan resistensi kompresi serta prioritas pelukan konten. Secara default, UIViewinstance tidak menentukan ukuran konten intrinsik. Ini adalah sesuatu yang umumnya disediakan oleh subkelas, seperti UILabelatau UIButton.

Karena resistensi kompresi konten dan prioritas pelukan konten dari dua UIViewinstance baru akan sama, dan tidak ada tampilan yang menyediakan ukuran konten intrinsik, mesin tata letak harus membuat tebakan terbaik untuk ukuran apa yang harus dialokasikan untuk setiap tampilan. Dalam kasus Anda, ini menetapkan tampilan pertama 100% dari ruang yang tersedia, dan tidak ada pada tampilan kedua.

Jika Anda mengubah kode Anda untuk menggunakan UILabelinstance, Anda akan mendapatkan hasil yang lebih baik:

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

Perhatikan bahwa Anda tidak perlu membuat kendala sendiri secara eksplisit. Ini adalah manfaat utama penggunaan UIStackView- menyembunyikan detail (seringkali jelek) dari manajemen kendala dari pengembang.

Greg Brown
sumber
Saya memiliki masalah yang sama bahwa ini berfungsi untuk Label, tetapi tidak untuk TextFields (atau Views, dalam hal ini). Juga semua tutorial yang saya temukan menggunakan Label, Gambar atau Tombol. Apakah ini berarti bahwa untuk hal yang berbeda dari elemen UI ini, kita tidak dapat menggunakan metode ini?
physicalattraction
@physicalattraction Tampilan yang Anda tambahkan ke tampilan tumpukan harus memiliki ukuran konten intrinsik. Seingat saya, ukuran intrinsik bidang teks didasarkan pada konten bidang (yang ganjil). Contoh UIViewitu sendiri tidak memiliki ukuran intrinsik. Anda mungkin perlu menerapkan batasan tambahan untuk tampilan ini sehingga tampilan tumpukan tahu seberapa besar membuatnya.
Greg Brown
Masuk akal, jadi saya mencobanya. Sekarang saya telah membangun sebuah View dalam xib dengan bidang teks, yang saya berikan batasan tinggi eksplisit 30 piksel. Selanjutnya saya membuat dua kendala berikut: MyTextField.top = top+20dan bottom = MyTextField.bottom+20. Saya berharap ini akan memberikan pandangan saya ketinggian intrinsik 70, tetapi malah mengeluh tentang kendala yang saling bertentangan. Apakah Anda tahu apa yang sedang terjadi di sini? Pandangan inilah yang ingin saya tempatkan di dalam UIStackView saya.
physicalattraction
Saya rasa saya tahu apa masalahnya. Tampilan tumpukan secara otomatis menempelkan subview yang diatur terakhirnya ke tepi bawahnya. Jadi tampilan tumpukan sedang mencoba untuk membuat wadah Anda melihat ukuran yang sama seperti itu sendiri. Namun, Anda telah mengatakan pada pandangan ini bahwa tingginya 70 piksel, yang menimbulkan konflik. Coba buat tampilan kosong tambahan segera setelah tampilan wadah Anda dan berikan prioritas memeluk konten vertikal dari 0. Berikan kontainer Anda melihat prioritas memeluk konten vertikal 1000. Itu akan menyebabkan tampilan kosong meregang untuk mengisi ruang kosong di tumpukan melihat.
Greg Brown
Saya berasumsi Anda menggunakan tampilan tumpukan vertikal. Anda mungkin juga ingin memeriksa artikel ini yang saya tulis tentang tampilan tumpukan: dzone.com/articles/…
Greg Brown
13

Dalam 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)])
Ashim Dahal
sumber
8

Anda harus mengatur jenis distribusi Anda. Dalam kode Anda, tambahkan saja:

self.stack1.distribution = UIStackViewDistributionFillEqually;

Atau Anda dapat mengatur distribusi langsung di pembangun antarmuka Anda. Sebagai contoh:

masukkan deskripsi gambar di sini

Semoga itu bisa membantu;) Lapinou.

Lapinou
sumber
8

Mengikuti dua baris memperbaiki masalah saya

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

Versi cepat -

    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
Ekta
sumber
8

Untuk jawaban yang diterima ketika Anda mencoba untuk menyembunyikan tampilan apa pun di dalam tampilan tumpukan, kendala berfungsi tidak benar.

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)>"
)

Alasannya adalah ketika menyembunyikan viewdi stackViewdalamnya akan mengatur ketinggian ke 0 untuk menghidupkannya.

Solusi mengubah kendala priorityseperti di bawah ini.

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
    }

}
William Hu
sumber
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

Ditingkatkan pada jawaban oleh @Oleg Popov

Arunabh Das
sumber
4

Dalam kasus saya, hal yang mengacaukan yang saya harapkan adalah bahwa saya melewatkan baris ini:

stackView.translatesAutoresizingMaskIntoConstraints = false;

Setelah itu tidak perlu mengatur batasan untuk subview saya yang diatur apa pun, stackview yang mengurusnya.

PakitoV
sumber
4

Swift 5 versi jawaban Oleg Popov , yang didasarkan pada jawaban user1046037 ini

//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
Abdurrahman Mubeen Ali
sumber
1

Saya baru saja menemukan masalah yang sangat mirip. Sama seperti yang disebutkan sebelumnya dimensi tampilan tumpukan bergantung pada satu ukuran konten intrinsik dari subview yang diatur. Ini solusi saya di Swift 2.x dan struktur tampilan berikut:

lihat - UIView

customView - CustomView: UIView

stackView - UISTackView

subview yang diatur - subkelas UIView khusus

    //: [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
Janusz Chudzynski
sumber
-2

Coba kode di bawah ini,

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

Semoga berhasil. Harap beri tahu saya jika Anda membutuhkan klarifikasi lagi

Nilesh Patel
sumber