วิธีที่“ ถูกต้อง” ในการจัดการการเปลี่ยนแปลงการวางแนวใน iOS 8 คืออะไร?


104

ใครช่วยบอกวิธีการทำงานกับอินเทอร์เฟซแนวตั้งและแนวนอนที่ "ถูกต้อง" หรือ "ดีที่สุด" ได้ไหม ดูเหมือนว่าฟังก์ชันทั้งหมดที่ฉันต้องการใช้เพื่อจุดประสงค์นั้นจะเลิกใช้แล้วใน iOS 8 และการวิจัยของฉันก็ไม่มีทางเลือกที่ชัดเจนและสวยงาม ฉันควรจะดูที่ความกว้างและความสูงจริงๆเพื่อพิจารณาตัวเองว่าเราอยู่ในโหมดแนวตั้งหรือแนวนอน?

ตัวอย่างเช่นในตัวควบคุมมุมมองของฉันฉันควรใช้ pseudocode ต่อไปนี้อย่างไร

if we are rotating from portrait to landscape then
  do portrait things
else if we are rotating from landscape to portrait then
  do landscape things

3
UIViewControllerอ่านเอกสารสำหรับ ดูหัวข้อ "การจัดการมุมมองการหมุนเวียน" ซึ่งจะอธิบายถึงสิ่งที่คุณควรทำ
rmaddy

การที่พวกเขาเลิกใช้งานเป็นเบาะแส คุณต้องใช้อย่างอื่น .... อย่างอื่นควรเป็น AutoLayout และ Size Classes :-)
Aaron

คำตอบ:


263

Apple ขอแนะนำให้ใช้คลาสขนาดเป็นการวัดพื้นที่หน้าจออย่างคร่าวๆเพื่อให้ UI ของคุณเปลี่ยนเค้าโครงและรูปลักษณ์ได้อย่างมาก พิจารณาว่า iPad ในแนวตั้งมีขนาดคลาสเดียวกันกับในแนวนอน (ความกว้างปกติความสูงปกติ) ซึ่งหมายความว่า UI ของคุณควรมีความคล้ายคลึงกันมากหรือน้อยระหว่างการวางแนวทั้งสอง

อย่างไรก็ตามการเปลี่ยนจากแนวตั้งเป็นแนวนอนใน iPad นั้นมีความสำคัญมากพอที่คุณอาจต้องทำการปรับแต่ง UI ให้เล็กลงแม้ว่าคลาสขนาดจะไม่เปลี่ยนแปลงก็ตาม เนื่องจากวิธีการที่เกี่ยวข้องกับการวางแนวอินเทอร์เฟซUIViewControllerได้เลิกใช้แล้วตอนนี้ Apple จึงแนะนำให้ใช้วิธีการใหม่ต่อไปนี้UIViewControllerเพื่อทดแทน:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // Code here will execute before the rotation begins.
    // Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Place code here to perform animations during the rotation.
        // You can pass nil or leave this block empty if not necessary.

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Code here will execute after the rotation has finished.
        // Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

    }];
}

เยี่ยมมาก! ตอนนี้คุณจะได้รับการติดต่อกลับก่อนการหมุนจะเริ่มต้นและหลังจากเสร็จสิ้น แต่จะรู้ได้อย่างไรว่าการหมุนเป็นแนวตั้งหรือแนวนอน?

Apple แนะนำให้คิดเกี่ยวกับการหมุนเป็นเพียงการเปลี่ยนแปลงขนาดของมุมมองหลัก ในคำอื่น ๆ ในระหว่างการหมุน iPad จากแนวตั้งแนวนอนคุณสามารถคิดว่ามันเป็นมุมมองในระดับรากเพียงแค่เปลี่ยนมันbounds.sizeจากไป{768, 1024} {1024, 768}เมื่อทราบสิ่งนี้แล้วคุณควรใช้วิธีการsizeส่งผ่านviewWillTransitionToSize:withTransitionCoordinator:ด้านบนเพื่อดูว่าคุณกำลังหมุนเป็นแนวตั้งหรือแนวนอน

หากคุณต้องการวิธีที่ราบรื่นยิ่งขึ้นในการย้ายรหัสเดิมไปยัง iOS 8 แบบใหม่ในการทำสิ่งต่างๆให้ลองใช้หมวดหมู่ง่ายๆนี้บน UIView ซึ่งสามารถใช้เพื่อพิจารณาว่ามุมมองเป็น "แนวตั้ง" หรือ "แนวนอน" ตาม ขนาด.

สรุป:

  1. คุณควรใช้คลาสขนาดเพื่อพิจารณาว่าเมื่อใดที่จะแสดง UI ที่แตกต่างกันโดยพื้นฐาน (เช่น UI แบบ "เหมือน iPhone" เทียบกับ UI ที่ "เหมือน iPad")
  2. หากคุณต้องการปรับ UI ให้เล็กลงเมื่อคลาสขนาดไม่เปลี่ยน แต่ขนาดคอนเทนเนอร์ของคุณ (มุมมองหลัก) ทำเช่นเมื่อ iPad หมุนให้ใช้การviewWillTransitionToSize:withTransitionCoordinator:โทรกลับใน UIViewController
  3. ทุกมุมมองในแอปของคุณควรตัดสินใจเกี่ยวกับเค้าโครงตามพื้นที่ที่ได้รับให้กับการจัดวางเท่านั้นปล่อยให้ลำดับชั้นของมุมมองตามธรรมชาติเรียงซ้อนข้อมูลนี้ลง
  4. ในทำนองเดียวกันอย่าใช้statusBarOrientation- ซึ่งโดยพื้นฐานแล้วเป็นคุณสมบัติระดับอุปกรณ์เพื่อพิจารณาว่าจะจัดวางมุมมองสำหรับ "แนวตั้ง" เทียบกับ "แนวนอน" หรือไม่ ควรใช้การวางแนวแถบสถานะโดยโค้ดที่เกี่ยวข้องกับสิ่งต่างๆUIWindowที่อาศัยอยู่ในระดับรากของแอปเท่านั้น

5
ตอบโจทย์สุด ๆ ! BTW วิดีโอ youtube ของคุณใน AL ให้ข้อมูลอย่างเหลือเชื่อ ขอบคุณสำหรับการแบ่งปัน ลองดูสิ! youtube.com/watch?v=taWaW2GzfCI
smileBot

2
ปัญหาเดียวที่ฉันพบคือบางครั้งคุณต้องการทราบการวางแนวอุปกรณ์และสิ่งนี้จะไม่แจ้งให้คุณทราบ บน iPad จะถูกเรียกก่อนแถบสถานะหรือค่าการวางแนวอุปกรณ์จะเปลี่ยนไป คุณไม่สามารถบอกได้ว่าผู้ใช้ถืออุปกรณ์ในแนวตั้งหรือแนวตั้งกลับหัว นอกจากนี้ยังเป็นไปไม่ได้ที่จะทดสอบเนื่องจากการล้อเลียนUIViewControllerTransitionCoordinatorเป็นฝันร้าย :-(
Darrarski

@Darrarski คุณพบวิธีแก้ปัญหาที่สวยงามเพื่อรับมือกับปัญหาเหล่านั้นหรือไม่
Xvolks

เพื่อให้อยู่ที่ไหนviewdidlayoutsubviewsแล้ว? ฉันคิดว่าviewdidlayoutsubviewsใช้เพื่อจับการเปลี่ยนแปลงที่ถูกผูกไว้เนื่องจากการหมุน คุณสามารถอธิบายเกี่ยวกับเรื่องนี้ได้หรือไม่?
น้ำผึ้ง

1
จะแยกความแตกต่างระหว่างแนวนอนซ้ายกับแนวนอนขวาได้อย่างไรหากเราไม่ใช้ Statusbarorientation? ฉันต้องการความแตกต่างนั้น นอกจากนี้ยังมีปัญหาอื่น ๆ ตามที่อธิบายไว้ที่นี่ - stackoverflow.com/questions/53364498/…
Deepak Sharma

17

จากคำตอบที่ละเอียดมาก (และเป็นที่ยอมรับ) ของสไมลีย์บอร์กนี่คือการปรับตัวโดยใช้ swift 3:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: nil, completion: {
        _ in
        self.collectionView.collectionViewLayout.invalidateLayout()
    })        
}

และในUICollectionViewDelegateFlowLayoutการนำไปใช้งาน

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // retrieve the updated bounds
    let itemWidth = collectionView.bounds.width
    let itemHeight = collectionView.bounds.height
    // do whatever you need to do to adapt to the new size
}

11

ฉันแค่ใช้ศูนย์การแจ้งเตือน:

เพิ่มตัวแปรการวางแนว (จะอธิบายในตอนท้าย)

//Above viewdidload
var orientations:UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation

เพิ่มการแจ้งเตือนเมื่อมุมมองปรากฏขึ้น

override func viewDidAppear(animated: Bool) {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: UIDeviceOrientationDidChangeNotification, object: nil)
}

ลบการแจ้งเตือนเมื่อมุมมองหายไป

override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIDeviceOrientationDidChangeNotification, object: nil) 
}

รับการวางแนวปัจจุบันเมื่อมีการแจ้งเตือน

func orientationChanged (notification: NSNotification) {
    adjustViewsForOrientation(UIApplication.sharedApplication().statusBarOrientation)
}

ตรวจสอบการวางแนว (แนวตั้ง / แนวนอน) และจัดการเหตุการณ์

func adjustViewsForOrientation(orientation: UIInterfaceOrientation) {
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
    {
        if(orientation != orientations) {
            println("Portrait")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
    else if (orientation == UIInterfaceOrientation.LandscapeLeft || orientation == UIInterfaceOrientation.LandscapeRight)
    {
       if(orientation != orientations) {
            println("Landscape")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
}

เหตุผลที่ฉันเพิ่มตัวแปรการวางแนวก็เพราะว่าเมื่อทำการทดสอบบนอุปกรณ์จริงการแจ้งเตือนการวางแนวจะถูกเรียกทุกครั้งที่มีการเคลื่อนไหวเล็กน้อยในอุปกรณ์ไม่ใช่เฉพาะเมื่อมันหมุน การเพิ่มคำสั่ง var และ if จะเรียกใช้โค้ดก็ต่อเมื่อมันเปลี่ยนไปอยู่ในทิศทางตรงกันข้าม


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

2
Apple ยังไม่ได้คำนึงถึงฮาร์ดแวร์ด้วย 8.0 บอกเลเยอร์ AVPreview ว่าไม่ต้องกังวลเรื่องการวางแนวหากนำเสนอผ่านตัวควบคุมมุมมอง ใช้งานไม่ได้ แต่ได้รับการแก้ไขใน 8.2
Nick Turner

superของviewDidAppearและviewWillDisappearควรจะเรียกว่า
Andrew Bogaevskyi

หมายเหตุ: UIDeviceOrientation! = UIInterfaceOrientation ในการทดลองของฉัน statusBarOrientation ไม่น่าเชื่อถือ
nnrales

2

จากมุมมองของ UI ฉันเชื่อว่าการใช้ Size Classes เป็นแนวทางที่แนะนำของ Apple สำหรับการจัดการอินเทอร์เฟซในทิศทางขนาดและสเกลที่แตกต่างกัน

ดูส่วน: ลักษณะอธิบายคลาสขนาดและมาตราส่วนของอินเทอร์เฟซที่นี่: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 เพิ่มคุณสมบัติใหม่ที่ช่วยให้จัดการกับขนาดหน้าจอและการวางแนวได้หลากหลายขึ้นมาก"

อันนี้เป็นบทความที่ดีเช่นกัน: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDIT อัปเดตลิงค์: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (เครดิต: Koen)


ใช่ดูเหมือนว่าไซต์ทั้งหมดของเขาจะหยุดทำงานในขณะนี้
Aaron

บทความย่อมเป็นมูลค่าการอ่านและนี่คือที่อยู่ที่ถูกต้อง: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
Uem

แต่ถ้าเราไม่ได้พูดถึงคลาสขนาดและ UI แต่เกี่ยวกับ AVFoundation เช่น เมื่อบันทึกวิดีโอในบางกรณีคุณต้องทราบการวางแนว viewControllers เพื่อตั้งค่าข้อมูลเมตาที่ถูกต้อง สิ่งนี้เป็นไปไม่ได้ง่ายๆ "เอนกประสงค์".
Julian F.Winert

นั่นไม่ใช่สิ่งที่ OP กำลังถาม / พูดถึง
แอรอน

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