การจัดมุมมองให้อยู่กึ่งกลางในมุมมองของมันโดยใช้ภาษารูปแบบภาพ


160

ฉันเพิ่งเริ่มเรียนรู้ AutoLayout สำหรับ iOS และดูภาษารูปแบบภาพ

ทุกอย่างทำงานได้ดียกเว้นสิ่งหนึ่ง: ฉันไม่สามารถรับมุมมองไปยังศูนย์ภายในกำกับ
เป็นไปได้ด้วย VFL หรือฉันจำเป็นต้องสร้างข้อ จำกัด ด้วยตนเอง?

คำตอบ:


232

ขณะนี้ไม่ดูเหมือนว่าจะเป็นไปได้ที่จะจัดมุมมองให้อยู่กึ่งกลางใน superview โดยใช้VFL เท่านั้น อย่างไรก็ตามเป็นเรื่องยากที่จะใช้สาย VFL เดี่ยวและข้อ จำกัด พิเศษเดียว (ต่อแกน):

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

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

มีใครคิดว่าคุณสามารถทำได้ (ซึ่งเป็นสิ่งที่ฉันคิดตอนแรกและลองเมื่อฉันเห็นคำถามนี้):

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

ฉันลองใช้รูปแบบที่แตกต่างกันของหลายข้อที่พยายามดัดโค้งตามความประสงค์ของฉัน แต่สิ่งนี้ดูเหมือนจะไม่ใช้กับผู้ดูแลแม้ว่าจะมีสาย VFL สองสายแยกกันสำหรับทั้งสองแกน ( H:|V:) จากนั้นผมก็เริ่มที่จะลองและแยกว่าเมื่อตัวเลือกไม่ได้นำไปใช้กับ VFL ดูเหมือนว่าจะไม่ใช้กับผู้ดูแลใน VFL และจะใช้กับมุมมองที่ชัดเจนใด ๆ ที่กล่าวถึงในสตริง VFL (ซึ่งน่าผิดหวังในบางกรณี)

ฉันหวังว่าในอนาคต Apple จะเพิ่มตัวเลือกใหม่บางประเภทเพื่อให้ตัวเลือก VFL คำนึงถึงผู้ดูแลแม้ว่าจะทำเฉพาะเมื่อมีเพียงมุมมองที่ชัดเจนเพียงอย่างเดียวนอกเหนือจากการกำกับดูแลใน VFL ทางออกก็อาจจะเป็นอีกทางเลือกหนึ่งผ่านเข้าสู่ VFL NSLayoutFormatOptionIncludeSuperviewที่บอกว่าสิ่งที่ชอบ:

จำเป็นต้องพูดฉันเรียนรู้มากมายเกี่ยวกับ VFL พยายามตอบคำถามนี้


2
ขอบคุณสำหรับคำตอบโดยละเอียด เหตุผลเดียวที่ฉันใช้ VFL คือกำจัดข้อ จำกัด ที่น่าเกลียดเหล่านั้น น่าเสียดายที่ Apple ยังไม่สนับสนุนสิ่งนี้ ...
Christian Schnorr

@larsacus คุณช่วยแสดงความคิดเห็นได้ไหมว่าทำไมคำตอบด้านล่างนี้จึงใช้ได้? มันทำให้ฉันนิ่งงัน
Matt G

8
คำตอบของ Evgeny ด้านล่างใช้งานได้เนื่องจากเอ็นจิ้นการโหลดอัตโนมัติจัดการ|และ[superview]แยกเอนทิตีภายในแม้ว่ามันจะเป็นตัวแทนของความหมายเดียวกัน โดยการใช้การหาค่าอัตโนมัติ[superview]จะรวมถึงวัตถุ superview ในการวัดการจัดตำแหน่งของมัน (ในกรณีที่คำตอบของเขาที่ลิงก์ github คือNSLayoutFormatAlignAllCenterY/ the Xmetric)
larsacus

@ larsacus ยอดเยี่ยมขอบคุณ!
Matt G

เรารู้หรือไม่ว่าข้อ จำกัด ยังคงใช้กับเวอร์ชันของ VFL ที่มาพร้อมกับ iOS 7.x ปัจจุบันหรือไม่?
Drux

138

ใช่เป็นไปได้ที่จะจัดมุมมองให้อยู่กึ่งกลางในมุมมองของมันด้วยภาษาของรูปแบบภาพ ทั้งในแนวตั้งและแนวนอน นี่คือตัวอย่าง:

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


15
นี่มันยอดเยี่ยมคุณคิดว่าคุณจะสามารถขยายและอธิบายว่าทำไมมันถึงได้ผล?
tapi

3
ฉันลงเอยด้วยการใช้บางสิ่งที่ใกล้เคียงกับคำตอบที่ทำเครื่องหมายไว้ แต่ +1 สำหรับสิ่งที่แนบมา github ในของคุณ
Rembrandt Q. Einstein

4
@Dan หากไม่ได้ผลสำหรับคุณคุณอาจต้องมีข้อ จำกัด ในเรื่องความกว้างและความสูงเช่นกัน VFL จะเป็นเช่นนี้สำหรับความกว้าง: @ "[label (== 200)]" และเช่นนี้สำหรับความสูง: @ "V: [label (== 200)]"
Jonathan Zhan

1
ทำไมผลลัพธ์จาก | และ [superview] แตกต่างกันอย่างไร นี่เป็นสิ่งที่ควรจะเป็น Radar'd หรือว่าเกิดอะไรขึ้นฉันไม่ได้ติดตาม
แมตต์จี

3
ไม่สามารถแยกวิเคราะห์รูปแบบข้อ จำกัด : ตัวเลือกปิดบังมุมมองที่ต้องการจัดแนวบนขอบแนวนอนซึ่งไม่ได้รับอนุญาตสำหรับเค้าโครงที่เป็นแนวนอน H: [superview] - (<= 1) - [label1]
grabner

82

ฉันคิดว่าการสร้างข้อ จำกัด ด้วยตนเองจะดีกว่า

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

ตอนนี้เราสามารถใช้NSLayoutAnchorเพื่อกำหนด AutoLayout โดยทางโปรแกรม:

(มีให้ใน iOS 9.0 และใหม่กว่า)

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

ฉันแนะนำให้ใช้SnapKitซึ่งเป็น DSL เพื่อให้การจัดวางอัตโนมัติทำได้ง่ายทั้งใน iOS และ macOS

view.snp.makeConstraints({ $0.center.equalToSuperview() })

1
ทำงานได้ดีที่สุดกับบริบทของฉัน - ไม่มี VFL แม้ว่า
ระดมสมอง

มันให้คำเตือนแก่ฉัน: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

เป็นไปได้อย่างที่สุดก็สามารถทำได้เช่นนี้:

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

เรียนรู้สิ่งนี้จากที่นี่ โค้ดด้านบนกึ่งกลาง subview ในมุมมองโดย Y แน่นอน

Swift3:

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

7

เพิ่มรหัสด้านล่างและให้ปุ่มของคุณอยู่ตรงกลางของหน้าจอ เป็นไปได้อย่างแน่นอน

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

1

ฉันรู้ว่ามันไม่ต้องการที่คุณต้องการ แต่แน่นอนคุณสามารถคำนวณระยะขอบและใช้พวกเขาเพื่อสร้างสตริงรูปแบบภาพ;)

อย่างไรก็ตามไม่มี น่าเสียดายที่มันเป็นไปไม่ได้ที่จะทำอย่างนั้น 'อัตโนมัติ' กับ VFL - อย่างน้อยยังไม่ได้


คุณสามารถใช้ NSString stringWithFormat: เพื่อสร้างสตริง VFL แบบไดนามิกที่รันไทม์
TRVD1707

1

ขอบคุณคำตอบจาก @Evgenii ฉันสร้างตัวอย่างเต็มรูปแบบในส่วนสำคัญ:

จัดกึ่งกลางสี่เหลี่ยมสีแดงตามแนวตั้งด้วยความสูงที่กำหนดเอง

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

คุณสามารถจัดมุมมองในแนวตั้งให้อยู่กึ่งกลางได้โดยใช้ VFL (ซึ่งไม่ง่ายนัก) หรือใช้ข้อ จำกัด ด้วยตนเอง อย่างไรก็ตามฉันไม่สามารถรวมทั้ง centerY และ height เข้าด้วยกันใน VFL เช่น:

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

ในข้อ จำกัด VFL ของโซลูชันปัจจุบันถูกเพิ่มแยกต่างหาก


0

สิ่งที่ต้องทำคือควรประกาศผู้ควบคุมดูแลในพจนานุกรม แทนการใช้ที่คุณใช้|@{@"super": view.superview};

และNSLayoutFormatAlignAllCenterXสำหรับแนวตั้งและNSLayoutFormatAlignAllCenterYแนวนอนเป็นตัวเลือก


0

คุณสามารถใช้มุมมองพิเศษ

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

centerXView.hidden = centerYView.hidden = YES;ถ้าคุณอยากให้มันเรียบร้อยแล้ว

PS: ฉันไม่แน่ใจว่าฉันสามารถเรียกมุมมองเพิ่มเติม "ตัวยึด" เพราะภาษาอังกฤษเป็นภาษาที่สองของฉัน มันจะได้รับการชื่นชมถ้าใครบางคนสามารถบอกฉันว่า


มีบางอย่างขาดหายไปที่นี่ คุณจะเรียกบล็อกที่อยู่ภายใต้การประกาศมุมมองได้อย่างไร ไม่ใช่รหัสทางกฎหมายตามที่คุณวางไว้
ishahak

0

สำหรับผู้ที่มาที่นี่เพื่อInterface Builderแก้ปัญหาตาม (Google ทำให้ฉันที่นี่) เพียงเพิ่มข้อ จำกัด ความกว้าง / ความสูง const และเลือกมุมมองย่อย -> ควบคุมการลากไปที่ superview -> เลือก "กึ่งกลางแนวนอน / แนวนอนใน superview"


0

นี่คือวิธีการของฉัน

//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])

-1

เพียงแค่ต้องบอกว่าคุณสามารถใช้สิ่งนี้แทนข้อ จำกัด :

child.center = [parent convertPoint:parent.center fromView:parent.superview];

4
เพียงแค่ต้องบอกว่านั่นไม่ใช่สิ่งที่เขาขอและมันจะไม่ทำงานถ้าคุณหมุนอุปกรณ์ปรับขนาดหน้าจอและอื่น ๆ
jeffjv

-1

@ igor-muzykaตอบในSwift 2 :

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

-3

แทนที่จะใช้ VFL คุณสามารถทำได้ในส่วนต่อประสานผู้สร้าง ในกระดานเรื่องราวหลัก ctrl + คลิกที่มุมมองที่คุณต้องการจัดกึ่งกลาง ลากไปที่ superview (ในขณะที่กดปุ่ม ctrl) แล้วเลือก "Center X" หรือ "Center Y" ไม่จำเป็นต้องใช้รหัส

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.