มีเหตุการณ์ปรับขนาด UIView หรือไม่


102

ฉันมีมุมมองที่มีแถวและคอลัมน์ของมุมมองภาพอยู่ในนั้น

หากมุมมองนี้ถูกปรับขนาดฉันจำเป็นต้องจัดเรียงตำแหน่งการดูภาพใหม่

มุมมองนี้เป็นมุมมองย่อยของมุมมองอื่นที่ได้รับการปรับขนาด

มีวิธีตรวจจับเมื่อมีการปรับขนาดมุมมองนี้หรือไม่


มุมมองถูกปรับขนาดเมื่อใด เมื่ออุปกรณ์หมุนหรือเมื่อผู้ใช้หมุนโดยใช้มัลติทัช?
Evan Mulawski

มุมมองนี้จะถูกปรับขนาดเมื่อผู้ใช้แตะที่ปุ่มบนมุมมองอื่น (มุมมองหลัก)
live-love

คำตอบ:


98

ตามที่ Uli แสดงความคิดเห็นไว้ด้านล่างวิธีที่เหมาะสมในการทำคือการแทนที่layoutSubviewsและจัดวาง imageView ที่นั่น

หากด้วยเหตุผลบางประการคุณไม่สามารถซับคลาสและลบล้างlayoutSubviewsได้การสังเกตboundsควรใช้งานได้แม้ว่าจะสกปรกก็ตาม ที่แย่กว่านั้นคือมีความเสี่ยงในการสังเกต - Apple ไม่รับประกันว่า KVO จะทำงานในคลาส UIKit อ่านการสนทนากับวิศวกรของ Apple ที่นี่: วัตถุที่เกี่ยวข้องจะถูกเผยแพร่เมื่อใด

คำตอบเดิม:

คุณสามารถใช้การสังเกตคีย์ - ค่า:

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];

และใช้:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == yourView && [keyPath isEqualToString:@"bounds"]) {
        // do your stuff, or better schedule to run later using performSelector:withObject:afterDuration:
    }
}

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

@uli เป็นพยานได้ดีว่าพวกเขาเปลี่ยนแปลงสิ่งนี้ไปเรื่อย ๆ อย่างไรก็ตามตอนนี้คุณควรใช้viewWillTransitionฯลฯ เป็นต้น
Dan Rosenstark

viewWillTransition ของสำหรับ UIViewControllers ไม่ใช่ UIView
Armand

layoutSubviewsยังคงใช้วิธีการที่แนะนำหากขึ้นอยู่กับขนาดปัจจุบันของมุมมองจำเป็นต้องเพิ่ม / ลบมุมมองย่อยที่แตกต่างกันและต้องเพิ่ม / ลบข้อ จำกัด ที่แตกต่างกันออกไปหรือไม่
Chris Prince

1
ฉันคิดว่าฉันแค่ตอบคำถามนี้สำหรับกรณีของฉัน เมื่อฉันทำการตั้งค่า (ขึ้นอยู่กับขนาด) ฉันต้องการในการลบล้างขอบเขตมันก็ใช้ได้ เมื่อฉันทำในรูปแบบ
Chris Prince

93

ในUIViewคลาสย่อยสามารถใช้ผู้สังเกตการณ์คุณสมบัติ :

override var bounds: CGRect {
    didSet {
        // ...
    }
}

หากไม่มีคลาสย่อยการสังเกตคีย์ - ค่าด้วยสมาร์ทคีย์ - พา ธจะทำ:

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}

2
@ อาลี่ทำไมคิดอย่างนั้น?
Rudolf Adamkovič

ในการเปลี่ยนแปลงเฟรมโหลด แต่ดูเหมือนว่าขอบเขตจะไม่ (ฉันรู้ว่ามันแปลกและบางทีฉันอาจจะมีอะไรผิดพลาด)
อาลี

3
@Ali: ตามที่มีการกล่าวถึงสองสามครั้งที่นี่: frameเป็นคุณสมบัติที่ได้รับและรันไทม์คำนวณ อย่าลบล้างสิ่งนี้เว้นแต่คุณจะมีเหตุผลที่ชาญฉลาดและรู้เหตุผลในการทำเช่นนั้น มิฉะนั้นงาน: ใช้งานboundsหรือ layoutSubviews(ดียิ่งขึ้น)
auco

3
เป็นวิธีที่ยอดเยี่ยมในการสร้างวงกลม ขอบคุณ! override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
SimplGy

4
ไม่รับประกันว่าการตรวจจับboundsหรือframeการเปลี่ยนแปลงจะทำงานได้ขึ้นอยู่กับตำแหน่งที่คุณวางมุมมองของคุณในลำดับชั้นของมุมมอง ฉันจะลบล้างlayoutSubviewsแทน ดูนี้และนี้คำตอบ
HuaTham

31

สร้างคลาสย่อยของ UIView และแทนที่เค้าโครง


11
ปัญหานี้ก็คือมุมมองย่อยไม่เพียง แต่สามารถเปลี่ยนขนาดของพวกเขาได้ แต่ยังสามารถทำให้ขนาดนั้นเคลื่อนไหวได้ เมื่อ UIView รันแอนิเมชั่นจะไม่เรียก layoutSubviews ในแต่ละครั้ง
David Jeske

1
@DavidJeske UIViewAnimationOptions มีตัวเลือกที่เรียกว่า UIViewAnimationOptionLayoutSubviews ฉันคิดว่านี่อาจช่วยได้ :)
Neal.Marlin

8

ปุ่มกด Swift 4 KVO - นี่คือวิธีที่ฉันตรวจจับการหมุนอัตโนมัติและย้ายไปที่แผงด้านข้างของ iPad ควรทำงานในมุมมองใด ๆ ต้องสังเกตชั้นของ UIView

private var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = view.layer.observe(\.bounds) { object, _ in
        print(object.bounds)
    }
    // ...
}
override func viewWillDisappear(_ animated: Bool) {
    observer?.invalidate()
    //...
}

วิธีนี้ใช้ได้ผลสำหรับฉัน ฉันเพิ่งเริ่มใช้ Swift และไม่แน่ใจเกี่ยวกับไวยากรณ์ \ .bounds ที่คุณใช้ที่นี่ นั่นหมายความว่าอย่างไรกันแน่?
WBuck

นี่คือการสนทนาเพิ่มเติม: github.com/ole/whats-new-in-swift-4/blob/master/…
Warren Stringer

.layerได้เคล็ดลับ! คุณรู้หรือไม่ว่าทำไมการใช้view.observeไม่ได้ผล?
d4Rk

@ d4Rk - ฉันไม่รู้ ฉันเดาว่าเลเยอร์นั้นมีความคลุมเครือน้อยกว่าเฟรม UIView ตัวอย่างเช่น contentOffset ของ UITableView จะส่งผลต่อพิกัดสุดท้ายของมุมมองย่อย ไม่แน่ใจ.
Warren Stringer

นี่เป็นคำตอบที่ดีสำหรับฉัน กรณีของฉันคือฉันใช้ AutoLayout ในการจัดวางมุมมองของฉัน แต่ UICollectionViewFlowLayout บังคับให้คุณระบุขนาดรายการที่ชัดเจน แต่เมื่อขนาด collectionView ของคุณเปลี่ยนไป (เช่นในกรณีของฉันเมื่อฉันแสดงแถบเครื่องมือด้วย AutoLayout) - itemSize ยังคงเหมือนเดิมและพ่น = [ผู้สังเกตการณ์เป็นแนวทางที่สะอาดที่สุดที่ฉันมีจนถึงตอนนี้ และสิ่งที่ดีที่สุด !!
Yitzchak

7

คุณสามารถสร้างคลาสย่อยของ UIView และแทนที่ไฟล์

setFrame: เฟรม (CGRect)

วิธี. นี่คือวิธีการที่เรียกว่าเมื่อเฟรม (เช่นขนาด) ของมุมมองเปลี่ยนไป ทำสิ่งนี้:

- (void) setFrame:(CGRect)frame
{
  // Call the parent class to move the view
  [super setFrame:frame];

  // Do your custom code here.
}

เหมาะสมและใช้งานได้ โทร.
El Zorko

1
FYI สิ่งนี้ใช้ไม่ได้กับคลาสย่อย UIImageView ข้อมูลเพิ่มเติมที่นี่: stackoverflow.com/questions/19216684/…
William Entriken

นอกจากนี้setFrame:ก็ไม่ได้เรียกร้องให้ฉันUITextViewsubclass ในระหว่างการปรับขนาดที่เกิดจาก autorotation ในขณะที่layoutSubviews:เป็น หมายเหตุ: ฉันใช้เค้าโครงอัตโนมัติและ iOS 7.0
ma11hew28

3
setFrame:ไม่เคยแทนที่ frameเป็นคุณสมบัติที่ได้รับ ดูคำตอบของฉัน
Adlai Holler

7

ค่อนข้างเก่า แต่ก็ยังเป็นคำถามที่ดี ในโค้ดตัวอย่างของ Apple และในคลาสย่อย UIView ส่วนตัวบางคลาสพวกเขาจะแทนที่ setBounds โดยประมาณเช่น:

-(void)setBounds:(CGRect)newBounds {
    BOOL const isResize = !CGSizeEqualToSize(newBounds.size, self.bounds.size);
    if (isResize) [self prepareToResizeTo:newBounds.size]; // probably saves 
    [super setBounds:newBounds];
    if (isResize) [self recoverFromResizing];
}

การลบล้างsetFrame:ไม่ใช่ความคิดที่ดี frameที่ได้มาจากcenter, boundsและtransformดังนั้น iOS setFrame:ของคุณจะไม่จำเป็นต้องโทร


2
นี่เป็นความจริง (ในทางทฤษฎี) อย่างไรก็ตามsetBounds:จะไม่ถูกเรียกเมื่อตั้งค่าคุณสมบัติของเฟรม (อย่างน้อยบน iOS 7.1) นี่อาจเป็นการเพิ่มประสิทธิภาพที่ Apple เพิ่มเพื่อหลีกเลี่ยงข้อความเพิ่มเติม
nschum

1
... และการออกแบบที่ไม่ดีในฝั่งของ Apple ที่จะไม่เรียกผู้เข้าถึงของตัวเอง
osxdirk

1
ในความเป็นจริงทั้งสอง frameและboundsจะได้มาจากมุมมองของพื้นฐานCALayer; พวกเขาเพียงแค่เรียกผ่านไปยัง getter ของเลเยอร์ และsetFrame:กำหนดกรอบของเลเยอร์ในขณะที่setBounds:กำหนดขอบเขตของเลเยอร์ ดังนั้นคุณจึงไม่สามารถลบล้างสิ่งใดสิ่งหนึ่งได้ นอกจากนี้ยังlayoutSubviewsถูกเรียกว่ามากเกินไป (ไม่ใช่แค่การเปลี่ยนแปลงรูปทรงเรขาคณิต) ดังนั้นจึงอาจไม่ใช่ตัวเลือกที่ดีเสมอไป ยังคงมองหา ...
big_m

-3

หากคุณอยู่ในอินสแตนซ์ UIViewController การลบล้างviewDidLayoutSubviewsจะเป็นการหลอกลวง

override func viewDidLayoutSubviews() {
    // update subviews
}

จำเป็นต้องเปลี่ยนขนาด UIView ไม่ใช่ UIViewController
Gastón Antonio Montes

ขอบคุณสำหรับสิ่งนั้น! คุณอาจสังเกตเห็นความสัมพันธ์ระหว่างUIViewอินสแตนซ์และUIViewControllerอินสแตนซ์ ดังนั้นหากคุณมีUIViewอินสแตนซ์ที่ไม่มี VC แนบมาคำตอบอื่น ๆ ก็เยี่ยมมาก แต่ถ้าคุณติด VC นี่คือผู้ชายของคุณ ขออภัยไม่สามารถใช้ได้กับกรณีของคุณ
Dan Rosenstark
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.