Memusatkan tampilan dalam superview menggunakan Bahasa Format Visual

160

Saya baru mulai belajar AutoLayout untuk iOS dan melihat-lihat Bahasa Format Visual.

Semuanya berfungsi dengan baik kecuali untuk satu hal: Saya tidak bisa mendapatkan pandangan ke pusat dalam superview-nya.
Apakah ini mungkin dengan VFL atau apakah saya perlu membuat sendiri batasan secara manual?

Christian Schnorr
sumber

Jawaban:

232

Saat ini, tidak, sepertinya tidak mungkin untuk memusatkan tampilan di superview hanya menggunakan VFL. Namun, tidak sulit untuk melakukannya dengan menggunakan string VFL tunggal dan satu kendala tambahan tunggal (per sumbu):

VFL: "|-(>=20)-[view]-(>=20)-|"

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeCenterX
                             relatedBy:NSLayoutRelationEqual
                                toItem:view.superview
                             attribute:NSLayoutAttributeCenterX
                            multiplier:1.f constant:0.f];

Orang akan berpikir bahwa Anda hanya dapat melakukan ini (yang awalnya saya pikirkan dan coba ketika saya melihat pertanyaan ini):

[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=20)-[view(==200)]-(>=20)-|"
                                 options: NSLayoutFormatAlignAllCenterX | NSLayoutFormatAlignAllCenterY
                                 metrics:nil
                                   views:@{@"view" : view}];

Saya mencoba banyak variasi berbeda dari yang di atas mencoba untuk menekuknya sesuai keinginan saya, tetapi ini tampaknya tidak berlaku untuk superview bahkan ketika secara eksplisit memiliki dua string VFL yang terpisah untuk kedua sumbu ( H:|V:). Saya kemudian mulai mencoba dan mengisolasi kapan tepatnya opsi yang bisa diterapkan pada VFL. Mereka tampaknya tidak berlaku untuk superview dalam VFL dan hanya akan berlaku untuk pandangan eksplisit yang disebutkan dalam string VFL (yang mengecewakan dalam kasus-kasus tertentu).

Saya berharap di masa depan Apple menambahkan beberapa jenis opsi baru agar opsi VFL memperhitungkan superview, bahkan jika melakukannya hanya ketika hanya ada satu tampilan eksplisit selain superview dalam VFL. Solusi lain bisa menjadi pilihan lain dilewatkan ke dalam VFL yang mengatakan sesuatu seperti: NSLayoutFormatOptionIncludeSuperview.

Tak perlu dikatakan, saya belajar banyak tentang VFL mencoba menjawab pertanyaan ini.

larsacus
sumber
2
Terima kasih atas jawaban terinci itu. Satu-satunya alasan bagi saya untuk menggunakan VFL, adalah menyingkirkan potongan-potongan kendala yang jelek itu. Sayang sekali Apple belum mendukung ini ...
Christian Schnorr
@ larsacus Bisakah Anda mengomentari mengapa jawaban di bawah ini berfungsi? Itu membuat saya bingung.
Matt G
8
Jawaban Evgeny di bawah berfungsi karena mesin autolayout memperlakukan |dan [superview]sebagai entitas internal yang terpisah meskipun mereka mewakili objek semantik yang sama. Dengan menggunakan [superview], autolayout termasuk objek superview dalam metrik penyelarasannya (dalam kasus ini dalam jawabannya di tautan github, the NSLayoutFormatAlignAllCenterY/ Xmetric).
larsacus
@ larsacus Luar Biasa, Terima kasih!
Matt G
Apakah kita tahu jika batasan masih berlaku dengan versi VFL yang disertakan dengan iOS 7.x saat ini?
Drux
138

Ya, dimungkinkan untuk memusatkan tampilan dalam superview dengan Bahasa Format Visual. Baik secara vertikal maupun horizontal. Inilah demo:

https://github.com/evgenyneu/center-vfl

Evgenii
sumber
15
Ini luar biasa, apakah Anda pikir Anda bisa mengembangkan ini dan menjelaskan mengapa itu berhasil?
tapi
3
Saya akhirnya menggunakan sesuatu yang lebih dekat dengan jawaban yang ditandai, tetapi +1 untuk lampiran github di milik Anda
Rembrandt Q. Einstein
4
@Dan Jika itu tidak berhasil untuk Anda, Anda mungkin perlu memiliki batasan untuk lebar dan tinggi juga. VFL akan menjadi seperti ini untuk lebar: @ "[label (== 200)]" dan seperti ini untuk tinggi: @ "V: [label (== 200)]"
Jonathan Zhan
1
Mengapa hasil dari | dan [superview] berbeda? Apakah ini sesuatu yang harusnya radar atau ada yang tidak saya ikuti?
Matt G
3
Tidak dapat mem-parsing format kendala: Opsi menutupi tampilan yang diperlukan untuk disejajarkan pada tepi horizontal, yang tidak diizinkan untuk tata letak yang juga horisontal. H: [superview] - (<= 1) - [label1]
grabner
82

Saya pikir lebih baik secara manual membuat batasan.

[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1
                               constant:0]];
[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterY
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterY
                             multiplier:1
                               constant:0]];

Sekarang kita dapat menggunakan NSLayoutAnchor untuk secara otomatis mendefinisikan AutoLayout:

(Tersedia di iOS 9.0 dan yang lebih baru)

view.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true

Saya sarankan menggunakan SnapKit , yang merupakan DSL untuk mempermudah Tata Letak Otomatis pada iOS dan macOS.

view.snp.makeConstraints({ $0.center.equalToSuperview() })
jqgsninimo
sumber
1
Saya bekerja dengan baik konteks saya - tidak ada VFL sekalipun.
brainray
itu memberi saya peringatan:Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
Tapas Pal
10

Sepenuhnya mungkin dapat melakukannya seperti ini:

[NSLayoutConstraint constraintsWithVisualFormat:@"H:[view]-(<=1)-[subview]"
                                        options:NSLayoutFormatAlignAllCenterY
                                        metrics:nil
                                          views:NSDictionaryOfVariableBindings(view, subview)];

Pelajari ini dari sini. Kode di atas memusatkan subview yang dilihat oleh Y jelas.

Swift3:

        NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=1)-[subview]",
                                       options: .alignAllCenterY,
                                                       metrics: nil,
                                                       views: ["view":self, "subview":_spinnerView])
Igor Muzyka
sumber
7

Tambahkan kode di bawah ini dan arahkan tombol Anda di tengah layar. Sangat mungkin.

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterY
                           metrics:0
                           views:views]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterX
                           metrics:0
                           views:views]];
Pankaj Gaikar
sumber
1

Saya tahu Anda tidak menginginkannya tetapi tentu saja Anda dapat menghitung margin dan menggunakannya untuk membuat string format visual;)

Bagaimanapun, tidak. Sayangnya itu tidak mungkin untuk melakukan itu 'secara otomatis' dengan VFL - setidaknya belum.

Tobi
sumber
Nah, Anda dapat menggunakan NSString stringWithFormat: untuk membangun string VFL Anda secara dinamis saat runtime.
TRVD1707
1

Berkat jawaban dari @Evgenii, saya membuat contoh lengkap di intinya:

pusat kotak merah secara vertikal dengan tinggi khusus

https://gist.github.com/yallenh/9fc2104b719742ae51384ed95dcaf626

Anda dapat memusatkan tampilan secara vertikal dengan menggunakan VFL (yang tidak terlalu intuitif) atau menggunakan kendala secara manual. Ngomong-ngomong, saya tidak bisa menggabungkan centerY dan tinggi bersama dalam VFL seperti:

"H:[subView]-(<=1)-[followIcon(==customHeight)]"

Dalam solusi saat ini kendala VFL ditambahkan secara terpisah.

Allen Huang
sumber
0

Yang harus dilakukan adalah bahwa superview harus dinyatakan dalam kamus. Alih-alih menggunakan yang |Anda gunakan @{@"super": view.superview};.

Dan NSLayoutFormatAlignAllCenterXuntuk NSLayoutFormatAlignAllCenterYopsi vertikal dan horizontal.

lagos
sumber
0

Anda dapat menggunakan tampilan ekstra.

NSDictionary *formats =
@{
  @"H:|[centerXView]|": @0,
  @"V:|[centerYView]|": @0,
  @"H:[centerYView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterY),
  @"V:[centerXView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterX),
};
NSDictionary *views = @{
  @"centerXView": centerXView, 
  @"centerYView": centerYView, 
  @"yourView": yourView,
};
 ^(NSString *format, NSNumber *options, BOOL *stop) {
     [superview addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:format
                                              options:options.integerValue
                                              metrics:nil
                                                views:views]];
 }];

Jika Anda menginginkannya rapi, maka centerXView.hidden = centerYView.hidden = YES;.

PS: Saya tidak yakin bisa menyebut pandangan ekstra "placeholder", karena bahasa Inggris adalah bahasa kedua saya. Ini akan dihargai jika seseorang bisa mengatakannya kepada saya.

CopperCash
sumber
Ada yang hilang di sini. Bagaimana Anda memanggil blokir di bawah deklarasi views ? Ini bukan kode hukum seperti yang Anda masukkan
ishahak
0

Bagi mereka yang datang ke sini untuk Interface Buildersolusi berbasis (Google membawa saya ke sini), cukup tambahkan lebar const / batasan ketinggian, dan pilih subview -> control drag ke superview itu -> pilih "pusatkan secara vertikal / horizontal dalam superview".

superarts.org
sumber
0

Ini metode saya

//create a 100*100 rect in the center of superView
var consts = NSLayoutConstraint.constraints(withVisualFormat: "H:|-space-[myView]-space-|", options: [], metrics:["space":view.bounds.width/2-50], views:["myView":myView])

consts += NSLayoutConstraint.constraints(withVisualFormat: "V:|-space-[myView]-space-|", options: [], metrics: ["space":view.bounds.height/2-50], views: ["myView":myView])
Calcifer
sumber
-1

Hanya perlu mengatakan, bahwa Anda dapat menggunakan ini alih-alih kendala:

child.center = [parent convertPoint:parent.center fromView:parent.superview];
Nik Kov
sumber
4
Hanya perlu mengatakan, bukan itu yang dia minta dan tidak akan berfungsi jika Anda memutar perangkat, ukuran layarnya diubah, dll.
jeffjv
-1

@ igor-muzyka menjawab di Swift 2 :

NSLayoutConstraint.constraintsWithVisualFormat("H:[view]-(<=1)-[subview]", 
                                               options: .AlignAllCenterY,
                                               metrics: nil,
                                               views: ["view":view, "subview":subview])
Federico Zanetello
sumber
-3

Alih-alih menggunakan VFL, Anda dapat dengan mudah melakukan ini di pembuat antarmuka. Di Main Storyboard, ctrl + klik pada tampilan yang Anda ingin pusatkan. Seret ke superview (sambil menahan ctrl) dan pilih "Center X" atau "Center Y". Tidak diperlukan kode.

Leenyburger
sumber