“ จาก View Controller” หายไปโดยใช้ UIViewControllerContextTransitioning


105

ฉันพบปัญหาหนึ่งและได้อธิบายไว้ด้านล่าง

ฉันใช้UIViewControllerContextTransitioningสำหรับการเปลี่ยนที่กำหนดเอง

ฉันมีตัวควบคุมมุมมอง 2 ตัวตัวควบคุมมุมมองแรกและตัวควบคุมมุมมองที่สอง

ตอนนี้ฉันต้องการเพิ่มตัวควบคุมมุมมองที่สองในตัวควบคุมมุมมองแรกที่มีภาพเคลื่อนไหว ฉันทำได้สำเร็จแล้วตอนนี้ตัวควบคุมมุมมองที่สองมีความโปร่งใสดังนั้นเราจึงเห็นตัวควบคุมมุมมองแรกด้านล่างตัวควบคุมมุมมองที่สอง

แต่ฉันมองไม่เห็นตัวควบคุมมุมมองแรกและฉันเห็นเฉพาะหน้าจอสีดำด้านล่างตัวควบคุมมุมมองที่สอง

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    self.transitionContext = transitionContext;
    if(self.isPresenting){
        [self executePresentationAnimation:transitionContext];
    }
    else{
       [self executeDismissalAnimation:transitionContext];
    }
  }

-(void)executePresentationAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{
     UIView* inView = [transitionContext containerView];
     UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

     UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

     CGRect offScreenFrame = inView.frame;
     offScreenFrame.origin.y = inView.frame.size.height;
     toViewController.view.frame = offScreenFrame;

    toViewController.view.backgroundColor = [UIColor clearColor];
    fromViewController.view.backgroundColor = [UIColor clearColor];
    inView.backgroundColor = [UIColor  clearColor];
    [inView insertSubview:toViewController.view aboveSubview:fromViewController.view];
     // [inView addSubview:toViewController.view];
    CFTimeInterval duration = self.presentationDuration;
    CFTimeInterval halfDuration = duration/2;

    CATransform3D t1 = [self firstTransform];
    CATransform3D t2 = [self secondTransformWithView:fromViewController.view];

    [UIView animateKeyframesWithDuration:halfDuration delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{

    [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.5f animations:^{
        fromViewController.view.layer.transform = t1;
    }];

    [UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.5f animations:^{
        fromViewController.view.layer.transform = t2;
    }];
    } completion:^(BOOL finished) {
    }];


    [UIView animateWithDuration:duration delay:(halfDuration - (0.3*halfDuration)) usingSpringWithDamping:0.7f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
        toViewController.view.frame = inView.frame;
    } completion:^(BOOL finished) {
        [self.transitionContext completeTransition:YES];
    }];
}

เมื่อถูก[self.transitionContext completeTransition:YES];เรียกจู่ ๆ ตัวควบคุมมุมมองแรกจะหายไปและหน้าจอสีดำจะแสดงด้านล่างตัวควบคุมมุมมองที่สอง

ใครมีความคิด? ขอบคุณ.

คำตอบ:


98

ผมมีปัญหาเดียวกันที่นี่ - ดูเหมือนว่าข้อผิดพลาดใน iOS 8. ฉันได้ยื่นเรดาร์

ฉันใช้Revealเพื่อตรวจสอบลำดับชั้นของมุมมองหลังจากที่หน้าจอเป็นสีดำ คีย์UIWindowว่างเปล่า - ไม่มีลำดับชั้นการดูเลย!

เปิดเผย

ฉันเล่นไปสักพักและดูเหมือนว่าจะมีวิธีแก้ปัญหาง่ายๆสำหรับกรณีง่ายๆ คุณสามารถเพิ่มtoViewControllerมุมมองอีกครั้งเป็นมุมมองย่อยของหน้าต่างหลัก:

transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)

ฉันได้ตรวจสอบแล้วและrootViewControllerยังคงตั้งค่าหน้าต่างหลักอย่างถูกต้องดังนั้นก็ไม่เป็นไร ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้นถ้าคุณนำเสนอคอนโทรลเลอร์จากภายในตัวควบคุมโมดอลที่นำเสนอไปแล้วดังนั้นสำหรับกรณีที่ซับซ้อนมากขึ้นคุณจะต้องทดลอง


2
ฉันยังได้เห็นปัญหานี้ iOS 8 แนะนำวิธีการและคีย์ใหม่สำหรับการเข้าถึง fromView และ toView (หมายเหตุ: ไม่ใช่ตัวควบคุมมุมมอง) ดูเหมือนว่าการอ้างอิงเหล่านี้จะไม่สูญหายไปในระหว่างการเปลี่ยนแปลง คุณสามารถเพิ่มลงในมุมมองคอนเทนเนอร์ได้ตามปกติหากคุณเพิ่งเรียกข้อมูลจากตัวควบคุมมุมมอง
tapi

1
ฉันเห็นความแปลกประหลาดของ iOS 8 ที่คล้ายกันเมื่อพยายามเพิ่มมุมมองย่อยในมุมมองของตัวควบคุมการนำทางของฉันใน viewDidLoad การเพิ่มมุมมอง navigationController อีกครั้งใน keyWindow ดูเหมือนจะทำเคล็ดลับได้ขอบคุณมาก Ash!
taber

1
ฉันยังคงเห็นสิ่งนี้ใน GM (และการแก้ไขนี้ยังใช้งานได้) คนอื่นเห็นเหมือนกันไหม นี่เป็นเพียงการเปลี่ยนแปลงใน API หรือไม่
rjkaplan

21
ฉันพบว่าข้อผิดพลาดนี้ (และอื่น ๆ อีกมากมาย) modalPresentationStyle = UIModalPresentationFullScreenหายไปถ้าคุณตั้งค่า แน่นอนคุณยังคงได้รับภาพเคลื่อนไหวการเปลี่ยนแปลงที่กำหนดเอง
คริส

1
ขอบคุณ @AshFurrow วิธีแก้ปัญหาที่ดีจนกว่าจะได้รับการแก้ไข!
kandelvijaya

78

ฉันรู้สึกว่าควรอธิบายเหตุผลเบื้องหลังสิ่งนี้ได้ดีกว่า

มุมมองจะหายไปเนื่องจากคุณนำมุมมองของตัวควบคุมมุมมองการนำเสนอออกจากตำแหน่งเดิม (ดูลำดับชั้น) วางไว้ใน containerView ที่แอนิเมเตอร์ของคุณมีให้ แต่จะไม่ส่งคืนกลับหลังจากภาพเคลื่อนไหวเสร็จสิ้น ดังนั้นมุมมองของตัวควบคุมมุมมองจะถูกลบออกด้วย superview (containerView) จากหน้าต่างอย่างสมบูรณ์

ใน iOS 7 ระบบจะส่งคืนมุมมองของตัวควบคุมมุมมองที่เกี่ยวข้องกับงานนำเสนอ (การนำเสนอและนำเสนอ) ไปยังตำแหน่งเดิมเสมอหลังจากการเปลี่ยนแปลงเสร็จสิ้นการเคลื่อนไหวโดยอัตโนมัติ ซึ่งจะไม่เกิดขึ้นกับรูปแบบการนำเสนอบางอย่างใน iOS 8 อีกต่อไป

กฎง่ายมาก: ภาพเคลื่อนไหวควรปรับมุมมองของตัวควบคุมมุมมองการนำเสนอถ้ามุมมองที่ควบคุมดูเป็นไปถูกซ่อนไว้ (ลบออกจากลำดับชั้นดู) อย่างสมบูรณ์โดยในตอนท้ายของการเปลี่ยนแปลง กล่าวอีกนัยหนึ่งก็หมายความว่าหลังจากภาพเคลื่อนไหวการนำเสนอเริ่มต้นเสร็จสิ้นเฉพาะมุมมองของตัวควบคุมมุมมองที่นำเสนอเท่านั้นที่จะมองเห็นได้ไม่ใช่มุมมองของตัวควบคุมมุมมองการนำเสนอ ตัวอย่างเช่นหากคุณตั้งค่าความทึบของมุมมองของตัวควบคุมมุมมองที่นำเสนอเป็น 50% และใช้ UIModalPresentationFullScreen คุณจะไม่สามารถเห็นมุมมองของตัวควบคุมมุมมองการนำเสนอที่อยู่ใต้การนำเสนอ แต่ถ้าคุณใช้ UIModalPresentationOverFullscreen คุณจะ ( shouldRemovePresentersViewวิธีการของ UIPresentationController รับผิดชอบในการระบุสิ่งนั้น)

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

ใน iOS 8 viewForKey:วิธีการได้รับการแนะนำเพื่อรับมุมมองที่อนิเมเตอร์จัดการ ขั้นแรกให้ปฏิบัติตามกฎที่อธิบายไว้ข้างต้นโดยการคืนค่าศูนย์เมื่อใดก็ตามที่แอนิเมเตอร์ไม่ควรสัมผัสกับมุมมอง ประการที่สองอาจส่งคืนมุมมองที่แตกต่างออกไปเพื่อให้แอนิเมเตอร์เคลื่อนไหวได้ ลองนึกภาพว่าคุณกำลังใช้งานนำเสนอที่คล้ายกับแบบฟอร์ม ในกรณีนี้คุณต้องการเพิ่มเงาหรือการตกแต่งรอบ ๆ มุมมองของตัวควบคุมมุมมองที่นำเสนอ แอนิเมเตอร์จะทำให้การตกแต่งนั้นเคลื่อนไหวแทนและมุมมองของตัวควบคุมมุมมองที่นำเสนอจะเป็นลูกของการตกแต่ง

viewControllerForKey: ไม่หายไป แต่ยังสามารถใช้ได้หากต้องการการเข้าถึงโดยตรงไปยังตัวควบคุมมุมมอง แต่แอนิเมเตอร์ไม่ควรตั้งสมมติฐานใด ๆ เกี่ยวกับมุมมองที่ต้องการเพื่อให้เคลื่อนไหว

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

  1. หากไม่จำเป็นต้องทำให้มุมมองของตัวควบคุมมุมมองการนำเสนอเป็นภาพเคลื่อนไหวให้ใช้viewForKey:เพื่อรับมุมมองเพื่อทำให้เคลื่อนไหวแทนที่จะยื่นออกไปเพื่อดูมุมมองของตัวควบคุมโดยตรง viewForKey:อาจคืนค่าศูนย์หรือแม้แต่มุมมองที่แตกต่างกันโดยสิ้นเชิง

  2. หากคุณต้องการให้เคลื่อนไหวมุมมองมุมมองการนำเสนอการควบคุมของคุณควรพิจารณาใช้UIModalPresentationFullScreenรูปแบบหรือยังคงใช้UIModalPresentationCustomและดำเนินการ subclass ของคุณเองกับ UIPresentationController กลับshouldRemovePresentersView YESในความเป็นจริงการใช้งานวิธีนี้เป็นข้อแตกต่างหลักระหว่างตัวควบคุมการนำเสนอภายในที่กำหนดโดยUIModalPresentationFullScreenและUIModalPresentationCustomสไตล์นอกเหนือจากข้อเท็จจริงที่ว่าหลังอนุญาตให้คุณใช้ตัวควบคุมการนำเสนอแบบกำหนดเองได้

  3. ในกรณีที่หายากอื่น ๆ ทั้งหมดคุณจะต้องคืนมุมมองของตัวควบคุมมุมมองการนำเสนอไปยังตำแหน่งเดิมตามคำตอบอื่น ๆ ที่แนะนำ


2
นั่นเป็นซุปเปอร์แปลกเพราะรหัสนี้ต้องอาศัยviewControllerForKey:'s views เฉพาะเมื่อviewForKey:ผลตอบแทน Nil และฉันยังคงต้องอีกครั้งเพิ่มไปที่หน้าต่างด้วยตนเอง คุณมีตัวอย่างของโค้ดที่ใช้งานได้โดยไม่มีวิธีแก้ปัญหานี้หรือไม่?
Ash Furrow

ถ้าviewForKey:คืนค่าเป็นศูนย์คุณจะต้องเพิ่มมุมมองของตัวควบคุมมุมมองการนำเสนออีกครั้งในหน้าต่างถ้าคุณลบออกจากมันในแอนิเมเตอร์ของคุณ ในกรณีที่ viewForKey ส่งคืนมุมมองของตัวควบคุมมุมมองจริงมันปลอดภัยที่จะย้ายมุมมองนั้นเนื่องจาก UIKit จะย้ายกลับไปที่ตำแหน่งเดิมหลังจากที่วงจรการนำเสนอสิ้นสุดลง
egdmitry

ขอขอบคุณที่อธิบายเหตุผลเบื้องหลังปัญหานี้ คุณพูดถูกจริงๆ การย้ายตำแหน่งของมุมมองในลำดับชั้นมุมมองโดยไม่แทนที่อย่างเห็นได้ชัดจะทำให้หายไป (โพสต์ iOS 8 และฉันกำลังทำงานกับ iOS 10 อยู่ในขณะนี้!) ขอบคุณสำหรับความกระจ่าง
Clay Ellis

1
ขอบคุณ egdmitry สำหรับคำชี้แจงของคุณ ซึ่งทำให้เกิดคำถามอีกข้อหนึ่งคือ: คุณคิดว่าฉันควรใช้งานการนำเสนอแบบเปิดเผยได้อย่างไร? หนึ่งในสิ่งที่พบบ่อยมากในปัจจุบันที่มุมมองการนำเสนอบางส่วนเลื่อนออกเพื่อแสดงมุมมองที่นำเสนอด้านล่าง? ในสถานการณ์นี้ทั้งมุมมองการนำเสนอและมุมมองที่นำเสนอจะต้องอยู่บนหน้าจอและมุมมองการนำเสนอเป็นภาพเคลื่อนไหว
Andrea

70

ใน iOS 8 คุณต้องจัดการกับมุมมองที่ส่งกลับโดยviewForKey:แทนที่จะเป็นทรัพย์สินของตัวควบคุมมุมมองที่ส่งกลับโดย.view viewControllerForKey:สิ่งนี้ไม่ชัดเจนโดยเฉพาะจากเอกสารเบต้า แต่ถ้าคุณดูแหล่งที่มาของ UIViewControllerTransitioningh คุณจะเห็นความคิดเห็นนี้ด้านบนviewControllerForKey::

// Currently only two keys are defined by the
// system - UITransitionContextToViewControllerKey, and
// UITransitionContextFromViewControllerKey.
// Animators should not directly manipulate a view controller's views and should
// use viewForKey: to get views instead.
- (UIViewController *)viewControllerForKey:(NSString *)key;

ดังนั้นแทนที่จะปรับเฟรม ฯลฯtoViewController.viewให้ใช้ค่าส่งคืนของ[transitionContext viewForKey:UITransitionContextToViewKey].

หากแอปของคุณต้องการรองรับ iOS7 และ / หรือ Xcode 5 คุณสามารถใช้วิธีการหมวดหมู่ง่ายๆบน UIViewController ดังต่อไปนี้:

- (UIView *)viewForTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
        NSString *key = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey] == self ? UITransitionContextFromViewKey : UITransitionContextToViewKey;
        return [transitionContext viewForKey:key];
    } else {
        return self.view;
    }
#else
    return self.view;
#endif
}

จากนั้นจะได้รับของคุณtoViewControllerและfromViewControllerตามปกติ [toViewController viewForTransitionContext:transitionContext]แต่จะได้รับมุมมองการใช้

แก้ไข: ดูเหมือนจะมีข้อบกพร่องโดยที่มุมมองของตัวควบคุมมุมมองการนำเสนอเป็นศูนย์เมื่อถูกส่งกลับviewForKeyซึ่งป้องกันไม่ให้คุณทำการเปลี่ยนโมดอลที่ทำให้มุมมองการนำเสนอเคลื่อนไหวได้เลย (เช่นเลื่อนออกหรือพลิกแนวนอน) ฉันยื่นข้อผิดพลาดสำหรับ iOS8 ที่ rdar: // 17961976 ( http://openradar.appspot.com/radar?id=5210815787433984 ) ดูโครงการตัวอย่างได้ที่http://github.com/bcherry/TransitionBug

แก้ไข 2: ขอขอบคุณ graveley สำหรับคำแนะนำการใช้ UIModalPresentationFullScreen ช่วยแก้ปัญหาได้ บางทีนี่อาจไม่ใช่ข้อผิดพลาด Apple อาจตั้งใจให้ UIModalPresentationCustom ปรับเปลี่ยนมุมมองของโมดอลที่เข้ามาเท่านั้น หากคุณต้องการแก้ไขมุมมองขาออกคุณต้องรับประกันการนำเสนอแบบเต็มหน้าจอของมุมมองใหม่หรือไม่ ไม่ว่าในกรณีใดคุณควรใช้viewForKeyและ UIModalPresentationFullScreen


2
ข้อผิดพลาด viewForKey ทำให้ฉันแทบคลั่ง! - ขอบคุณสำหรับการยื่น FWIW การเปลี่ยนแปลงของฉันทำงานได้ดีโดยรับมุมมองจาก UITransitionContextToViewControllerKey แต่การเปลี่ยนแปลงของฉันใช้การแปลงกับมุมมองทั้งหมดเท่านั้น ฉันไม่แน่ใจว่าควรตีความว่าเป็นmanipulatingมุมมอง VCs หรือไม่ ...
MathewS

1
ว้าว - นั่นคือถั่ว ฉันไม่เห็นสิ่งนั้นในความแตกต่าง - อาจเป็นเพราะมันเป็นเพียงความคิดเห็นเล็กน้อย น่าหงุดหงิดจริงๆเมื่อ Apple ดึงสตั๊นท์แบบนี้ นิ้วไขว้เกี่ยวกับเรดาร์ของคุณ
Ash Furrow

ฉันเห็นviewForKeyข้อบกพร่องใน GM เช่นกัน เป็นคนอื่นด้วยหรือเปล่า? คุณพบวิธีแก้ปัญหาที่สมเหตุสมผลหรือไม่?
rjkaplan

2
ฉันคิดว่าตามความคิดเห็นของ- viewForKey// viewForKey: อาจคืนค่าศูนย์ซึ่งจะบ่งชี้ว่าแอนิเมเตอร์ไม่ควรปรับเปลี่ยนมุมมองของคอนโทรลเลอร์มุมมองที่เกี่ยวข้อง การกลับมาnilไม่ใช่จุดบกพร่อง
Ken Kuan

4
@kenKuan คุณอาจจะพูดถูก เมื่อใช้ UIModalPresentationFullScreen viewForKeyจะคืนค่าจากมุมมองและมุมมองไปยังมุมมอง ดังนั้นอาจเป็นความตั้งใจที่จะคืนค่าศูนย์สำหรับ UIModalPresentationCustom ฉันกำลังอัปเดตรายงานข้อบกพร่องและจะโพสต์กลับที่นี่หากได้รับการตอบกลับจาก Apple เกี่ยวกับเรื่องนี้
bcherry

24

ไม่ได้ตั้งค่าmodalPresentationStyleเป็น UIModalPresentationCustom แก้ไขปัญหาให้ฉัน

กล่าวอีกนัยหนึ่งการปล่อยให้เป็นค่าเริ่มต้นของ UIModalPresentationFullScreen แทนที่จะระบุ UIModalPresentationCustom แก้ไขปัญหามุมมองที่หายไป โปรดสังเกตว่าโพรโทคอล UIViewControllerTransitioningDelegate ยังคงถูกติดตามแม้ว่าจะปล่อยให้เป็นค่าเริ่มต้นก็ตาม ถ้าฉันจำได้อย่างถูกต้องกาลครั้งหนึ่ง UIModalPresentationCustom เป็นข้อกำหนด

จนถึงขณะนี้ใช้งานได้เฉพาะสำหรับภาพเคลื่อนไหวที่ไม่โต้ตอบเท่านั้น


1
ว้าว. นั่นมัน! ฉันทดสอบโดยไม่มี modalPresentationStyle ใน iOS7 และ 8 และใช้งานได้ทั้งสองอย่าง ขอบคุณ !!
Ah Ryun Moon

1
ขอบคุณ! สิ่งนี้รวมกับการใช้viewForKey:แทน.viewการviewControllerForKey:แก้ไขปัญหาทั้งหมดสำหรับฉัน
bcherry

1
สิ่งนี้แก้ไขปัญหาให้ฉันโดยไม่ใช้ viewForKey แต่ฉันคิดว่าควรใช้เช่นกัน
Kevin Sliech

5
แม้ว่าจะดูเหมือนจะช่วยแก้ปัญหาได้ แต่สิ่งสำคัญคือต้องสังเกตว่าหน้าจอด้านหลังตัวควบคุมมุมมองของคุณจะกลายเป็นสีดำเมื่อแสดงขึ้น นี่เป็นสิ่งสำคัญหากตัวควบคุมมุมมองของคุณไม่ใช่แบบเต็มหน้าจอ
เพื่อน

16

ฉันพบคำตอบที่มีประโยชน์อย่างยิ่งนี้ในหัวข้อที่เกี่ยวข้องโดย Lefteris: https://stackoverflow.com/a/27165723/3709173

สรุปได้:

  1. ตั้งค่า modalPresentationStyle เป็น. Custom
  2. UIPresentationController คลาสย่อยแทนที่ shouldRemovePresentersView (กับ NO)
  3. แทนที่ presentationControllerForPresentedViewController ในคลาส TransitionDelegate ของคุณและส่งคืน UIPresentationController ที่คุณกำหนดเอง

+1 ในการเปลี่ยนแปลงที่กำหนดเองของคุณอย่าเพิ่ม toView เมื่อเกิดภาพเคลื่อนไหวการปิด

แสดงที่นี่:

https://www.dropbox.com/s/7rpkyamv9k9j18v/CustomModalTransition.zip?dl=0 โดยไม่ต้องแฮ็ค! มันเหมือนเวทมนตร์! :)


1
นี่คือคำตอบที่แท้จริง ไม่มีเล่ห์กลเหมือนในที่ยอมรับ ขอบคุณมาร์ค!
Andrei Malygin

น่าเสียดายที่สิ่งนี้ใช้ไม่ได้ใน iOS 12.4, Xcode 10.3 หน้าจอจะเปลี่ยนเป็นสีดำหลังจากการเปลี่ยนแปลงเสร็จสิ้น (มุมมองทั้งหมดถูกลบออกจากลำดับชั้นอย่างไรก็ตามการตั้งค่าคุณสมบัติ "modalPresentationStyle" เป็น ".fullscreen" จะทำงานได้ไชโย
Womble

ฉันลองใช้เวอร์ชัน Obj-C จากการใช้งาน Swift ของ Mark และ gwinyai ในโครงการของฉัน น่าเสียดายที่ไม่มีการทำงานตามที่คาดไว้ ฉันใช้ Xcode 11.1 และสร้างเป้าหมายคือ iOS 13.0 ฉันลองทั้งบนอุปกรณ์และตัวจำลอง ในกรณีของฉันการตั้งค่าพื้นฐานของฉันคือมุมมองคอลเลกชันและเมื่อคุณแตะหนึ่งเซลล์มันจะต่อเนื่องกับมุมมองรายละเอียดพร้อมภาพเคลื่อนไหว อย่างไรก็ตามมันใช้งานได้ดีโดยสิ้นเชิงหากฉันใช้ภาพเคลื่อนไหวการเปลี่ยนค่าเริ่มต้น VC ที่นำเสนอจะไม่หายไปเมื่อฉันดำเนินการต่อจากรายละเอียดมุมมอง
infinity_coding7

8

ใน iOS 8 คุณต้องสร้าง UIPresentationController และใช้วิธีการด้านล่างใน UIViewControllerTransitioningDelegate

- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source;

ขอให้ผู้รับมอบสิทธิ์ของคุณสำหรับตัวควบคุมการนำเสนอแบบกำหนดเองเพื่อใช้สำหรับการจัดการลำดับชั้นของมุมมองเมื่อนำเสนอตัวควบคุมมุมมอง

มูลค่าผลตอบแทน:

ตัวควบคุมการนำเสนอแบบกำหนดเองสำหรับจัดการการนำเสนอแบบโมดอล

อภิปรายผล:

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

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

ความพร้อมใช้งานมีให้ใน iOS 8.0 และใหม่กว่า

สำหรับข้อมูลเพิ่มเติมโปรดดูวิดีโอ WWDC 2014:

https://developer.apple.com/videos/wwdc/2014/?include=228

นอกจากนี้ยังมีโค้ดตัวอย่างจาก WWDC ที่เรียกว่า "LookInside: Presentation Controllers Adaptivity and Custom Animator Objects" ซึ่งคุณสามารถดาวน์โหลดได้จากหน้าโค้ดตัวอย่าง WWDC 2014

คุณอาจต้องเปลี่ยนโค้ดตัวอย่างเล็กน้อย วิธีการเริ่มต้น UIPresentationController เปลี่ยนเป็น:

initWithPresentedViewController:presented presentingViewController:presenting

ก่อนที่จะนำเสนอแล้วจึงนำเสนอ เพียงแค่สลับมันและควรใช้งานได้


ขออภัยที่ไม่ได้ดูวิดีโอที่เชื่อมโยง แต่ฉันไม่คิดว่าคุณต้องการ UIPresentationController ที่กำหนดเองเว้นแต่คุณต้องการการนำเสนอที่ไม่ได้มาตรฐานหลังจากที่ภาพเคลื่อนไหวเสร็จสิ้นเช่นมุมมองที่นำเสนอแบบวงกลม หากคุณต้องการเพียงแค่ภาพเคลื่อนไหวที่แตกต่างกันการใช้ UIViewControllerAnimateTransitioning ก็น่าจะเพียงพอแล้วโดยอาศัยความรู้ที่ จำกัด ของฉัน
Vaddadi Kartick

7

แทนที่จะเป็น [inView insertSubview: toViewController.view aboveSubview: fromViewController.view]; เพียงเพิ่ม: [inView addSubview: toViewController.view];

if (self.presenting) {

    [transitionContext.containerView addSubview:toViewController.view];
    // your code

} else {
    // your code
}

คุณสามารถดูตัวอย่างได้ที่นี่: ลิงค์และใช้งานได้บน iOS 7 และ iOS 8


นี่ควรเป็นคำตอบที่ยอมรับสำหรับการทำแอนิเมชั่นประเภท UIModalPresentationStyleCustom เนื่องจากไม่จำเป็นต้องเพิ่ม fromViewController ลงใน containerView คุณจะต้องเพิ่ม toViewController ระหว่างการนำเสนอภาพเคลื่อนไหวเท่านั้น
Scott Kaiser

สิ่งนี้มีประโยชน์มากจริง ๆ
Dmitry Bondarev

7

นี่คือเวอร์ชัน Objective C ของการแก้ไขของ Ash

// my attempt at obj-c version of Ash's fix
UIView *theToView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
[[[UIApplication sharedApplication] keyWindow] addSubview:theToView];
[transitionContext completeTransition:YES]

ฉันต้องสลับคำสั่งซื้อและเรียกใช้เมธอด [transitionContext completeTransition:] หลังจากเพิ่มมุมมองกลับเข้าไปเพื่อนำเสนอตัวควบคุมมุมมองใหม่จากบล็อกการเลิกใช้งานของคอนโทรลเลอร์มุมมองอื่นให้ทำงานได้อย่างถูกต้อง

ฉันไม่รู้ว่าสิ่งนี้จะแก้ไขได้สำหรับทุกคน แต่ใช้ได้ในแอปของฉัน ไชโย!


5

ฉันพบว่าสิ่งนี้ใช้งานได้ดีสำหรับ Obj-C:

    [transitionContext completeTransition:YES];
    if(![[UIApplication sharedApplication].keyWindow.subviews containsObject:toViewController.view]) {
        [[UIApplication sharedApplication].keyWindow addSubview:toViewController.view];
    }

ดูเหมือนว่าจะทำงานได้ดีทั้งบน ios7 และ ios8


5

ฉันพบว่าviewForKey:UITransitionContextToViewKeyคืนค่า nil บน ios8 ดังนั้นถ้าไม่มีฉันจะจับมุมมองจากตัวควบคุมมุมมอง 'ถึง'

อย่างไรก็ตามดูเหมือนว่าจะส่งผลให้มุมมอง "ถึง" ไม่ถูกย้ายจากคอนเทนเนอร์ไปยังหน้าต่างเมื่อcompleteTransition:YESมีการเรียกใช้ ดังนั้นหากviewForKey:UITransitionContextToViewKeyคืนค่าศูนย์ฉันจะไปtoVC.viewและติดตามความจริงที่ว่ามันคืนค่าศูนย์และหลังจากเสร็จสิ้นฉันย้ายไปที่ superview เริ่มต้นของคอนเทนเนอร์ (ซึ่งเป็นหน้าต่าง)

ดังนั้นรหัสนี้จึงใช้ได้กับ iOS7 และ iOS8 และควรใช้กับ iOS9 ได้เช่นกันแม้ว่าจะแก้ไขได้หรือไม่ก็ตาม

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    // Get the 'from' and 'to' views/controllers.
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    BOOL hasViewForKey = [transitionContext respondsToSelector:@selector(viewForKey:)]; // viewForKey is iOS8+.
    UIView *fromView = hasViewForKey ?
        [transitionContext viewForKey:UITransitionContextFromViewKey] :
        fromVC.view;
    UIView *toView = hasViewForKey ?
        [transitionContext viewForKey:UITransitionContextToViewKey] :
        toVC.view;

    // iOS8 has a bug where viewForKey:to is nil: http://stackoverflow.com/a/24589312/59198
    // The workaround is: A) get the 'toView' from 'toVC'; B) manually add the 'toView' to the container's
    // superview (eg the root window) after the completeTransition call.
    BOOL toViewNilBug = !toView;
    if (!toView) { // Workaround by getting it from the view.
        toView = toVC.view;
    }
    UIView *container = [transitionContext containerView];
    UIView *containerSuper = container.superview; // Used for the iOS8 bug workaround.

    // Perform the transition.
    toView.frame = container.bounds;
    [container insertSubview:toView belowSubview:fromView];
    [UIView animateWithDuration:kDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        fromView.frame = CGRectOffset(container.bounds, 0, CGRectGetHeight(container.bounds));
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:YES];

        if (toViewNilBug) {
            [containerSuper addSubview:toView];
        }
    }];
}

3

ฉันพบว่าข้อผิดพลาดนี้ (และอื่น ๆ อีกมากมาย) modalPresentationStyle = UIModalPresentationFullScreenหายไปถ้าคุณตั้งค่า แน่นอนคุณยังคงได้รับภาพเคลื่อนไหวการเปลี่ยนแปลงที่กำหนดเอง


2

ฉันติดปัญหานี้เหมือนกัน ฉันต้องการสร้างการเปลี่ยนแปลงที่กำหนดเองโดยใช้พื้นหลังแบบกึ่งโปร่งใสซึ่งฉันยังคงเห็นตัวควบคุมมุมมองที่ฉันมาจาก แต่ฉันมีเพียงพื้นหลังสีดำ ฉันพบว่าคำตอบของ Mark Aron ในหัวข้อนี้ช่วยฉันได้ แต่มันถูกเขียนใน Objective C ดังนั้นนี่คือคำตอบเวอร์ชัน Swift 3 ที่ฉันได้ทดสอบสำหรับ iOS 9 และ iOS 10:

  1. สร้างคลาสย่อยของ UIPresentationController แทนที่ shouldRemovePresentersView เป็น false ดังนี้:

    class ModalPresentationController: UIPresentationController {
    
    override var shouldRemovePresentersView: Bool {
    return false
    }
    
    override func containerViewWillLayoutSubviews() {
    presentedView?.frame = frameOfPresentedViewInContainerView
    }
    }
  2. ในสถานที่ที่คุณกำลังสร้างอินสแตนซ์ตัวควบคุมมุมมองใหม่และตั้งค่าผู้รับมอบสิทธิ์การเปลี่ยนให้ระบุว่าคุณต้องการให้แสดงรูปแบบการนำเสนอโมดอลแบบกำหนดเองดังนี้:

    let newVC = mainStoryboard.instantiateViewController(withIdentifier: "newVC") as! NewViewController 
    
    newVC.transitioningDelegate = self
    
    newVC.modalPresentationStyle = UIModalPresentationStyle.custom
    
    newVC.modalPresentationCapturesStatusBarAppearance = true //optional
    
    present(newVC, animated: true, completion: nil)
  3. ตอนนี้แทนที่เมธอด presentationController ของ UIViewControllerTransitioningDelegate ของคุณและส่งคืน UIPresentationController ที่คุณกำหนดเอง ฉันมีของฉันเป็นส่วนขยายของชั้นเรียนปัจจุบันของฉัน:

    extension CurrentViewController: UIViewControllerTransitioningDelegate {
    
    //this is where you implement animationController(forPresented) and animationController(forDismissed) methods
    
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    
    return ModalPresentationController(presentedViewController: presented, presenting: source)
    
    }
    }

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


iis นี้เป็นตัวอย่างที่ยอดเยี่ยมสำหรับการนำเสนอโมดอลแบบกำหนดเองใน Swift 3! ขอบคุณ @gwinyai! ฉันติดอยู่กับสิ่งนี้มากจนกระทั่งฉันพบตัวอย่างที่แสดง swift 3 API ใหม่presentationController(forPresented presented UIViewController,... เนื่องจาก Swift API ก่อนหน้านี้ไม่ทำให้ผู้ปฏิบัติตาม แต่ไม่ได้รับการเรียก
Natalia

2

หลังจากพบปัญหานี้ฉันรู้สึกสับสนมากเพราะฉันได้เขียนสิ่งที่เกือบจะเหมือนกันเมื่อไม่นานมานี้ซึ่งใช้งานได้ดี มาที่นี่เพื่อค้นหาคำตอบเพื่อค้นหาการแก้ไขที่ดูแฮ็กและดูเหมือนจะไม่เข้าใจสาเหตุที่แท้จริง ... มันง่ายมากที่จะแก้ไข

คำตอบบางอย่างพูดถึงการเปลี่ยนแปลงไปmodalPresentationStyle .overFullScreenถูกต้องแล้ว.overCurrentContextก็ใช้ได้เช่นกัน นี่เป็นสิ่งที่คาดหวังและพฤติกรรมของ Apple แต่ทำไมถึงไม่ได้ผลกับทุกคน? ทำไมโค้ดแฮ็กทั้งหมดและการรวมกันของสิ่งนี้กับสิ่งอื่นและสิ่งที่บ้าคลั่งที่คุณไม่ควรทำ?

เปิดออกคุณจะต้องตั้งรูปแบบการนำเสนอก่อนที่จะโหลด VIEW ไม่หลังจากนั้น ทำใน init หรือทำจากคอนโทรลเลอร์ก่อนหน้าหรือตามที่คุณต้องการตราบเท่าที่ยังอยู่ก่อนที่จะโหลดมุมมอง


1
ฉันตั้งค่ารูปแบบการนำเสนอเป็น.overCurrentContext ก่อนโหลดมุมมอง (ในinitตัวควบคุมมุมมอง) และปัญหายังคงเกิดขึ้น
ricardopereira

1

การใช้ UIModalPresentationOverCurrentContext ใหม่แก้ไขให้ฉัน การเปลี่ยนแปลงเดิมของฉันใน iOS 7 คือการมีพื้นหลังเบลอของมุมมองใต้โมดอล


ด้วยเหตุผลบางประการดูเหมือนว่าจะไม่อนุญาตให้มีการโต้ตอบกับมุมมองด้านล่างซึ่ง UIModalPresentationCurrentContext ทำใน iOS 7 .. มีความคิดอย่างไร
Christopher Wirt

อืมสำหรับฉันใน iOS 10, .overCurrentContext ส่งผลให้เกิดข้อผิดพลาดนี้ แต่. fullscreen ไม่ทำ ฉันมาที่นี่โดยหวังว่าจะได้รับการแก้ไขสำหรับการใช้. overCurrentContext แต่จนถึงขณะนี้ดูเหมือนว่าจะไม่มีอะไรทำงานใน iOS 10 ยกเว้นอาจจะเป็น UIPresentationController คลาสย่อย ...
Natalia

0

ตกลงพวกฉันคิดว่าฉันแก้ปัญหาหนึ่งกรณีที่ 'แอนิเมเตอร์ที่ใช้งานได้' หยุดทำงานอย่างถูกต้องเมื่อคุณสร้างแอพใน iOS 13 ขึ้นไป

Env Xcode 11.1, iOS 13.1

ปัญหา

สิ่งที่ฉันต้องการทำนั้นง่ายมาก: ฉันมีมุมมองคอลเลกชันเมื่อมีการแตะเซลล์มันจะต่อเนื่องกับมุมมองรายละเอียด แทนที่จะใช้รูปแบบเริ่มต้นที่น่าเบื่อของ 'present modally' ฉันต้องการทำให้น่าสนใจยิ่งขึ้นฉันจึงเขียนแอนิเมเตอร์สำหรับการเปลี่ยนมุมมองของคอนโทรลเลอร์

ฉันตั้งค่าการทำต่อใน IB โดยการลากแล้วปล่อยจาก VC คอลเลกชันของฉันไปยัง VC รายละเอียด รูปแบบของการต่อคือ 'นำเสนอแบบโมดอล' และการนำเสนอถูกตั้งค่าเป็น 'เต็มหน้าจอ'

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

1. หลังจากบรรทัดต่อไปนี้ถูกเรียกใช้จากฟังก์ชัน 'animateTransition ()' มุมมองคอลเลกชันจะดำเนินการต่อและแสดงขึ้น

transitionContext.completeTransition(true)

2. ตราบใดที่มุมมองรายละเอียดยังไม่ครอบคลุมมุมมองคอลเลคชันทั้งหมดมุมมองคอลเลกชันจะไม่หายไปเมื่อกลับจากมุมมองรายละเอียด

สารละลาย

พูดตามตรงฉันรู้เพียงเล็กน้อยว่าการเปลี่ยนภาพเคลื่อนไหวทำงานอย่างไร ดังนั้นฉันจึงสามารถติดตามโพสต์นี้และอีกข้อหนึ่งลองคำตอบแต่ละข้อ น่าเสียดายที่ไม่มีสิ่งใดที่เหมาะกับฉัน ในที่สุดฉันก็มาถึงจุดที่สิ่งเดียวที่ฉันสามารถปรับเปลี่ยนได้คือรูปแบบการนำเสนอของการทำต่อใน IB (ซึ่งฉันควรจะทำตั้งแต่เริ่มต้น) เมื่อฉันตั้งค่างานนำเสนอเป็น 'Over Full Screen' ปาฏิหาริย์จะเกิดขึ้นและปัญหาของฉันจะได้รับการแก้ไข มุมมองรายละเอียดสามารถแสดงแบบเต็มหน้าจอพร้อมภาพเคลื่อนไหวและเมื่อปิดฉันสามารถเห็นทั้งมุมมองคอลเลคชันเป็นพื้นหลังและมุมมองรายละเอียดแบบเคลื่อนไหว

จากนั้นอีกหนึ่งการค้นพบตามท้องถนน

3. ในการอ้างถึง 'toView' และ 'fromView' ทั้งสองวิธีต่อไปนี้ใช้ได้ผล

ทางอ้อม:

transitionContext.viewController(forKey: .to)?.view
transitionContext.viewController(forKey: .from)?.view

ทางตรง:

transitionContext.view(forKey: .to)
transitionContext.view(forKey: .from)

แต่เมื่อฉันเปลี่ยนรูปแบบการทำต่อเป็น 'Over Full Screen' การคืนค่า 'nil' โดยตรงสำหรับทั้ง 'toView' และ 'fromView' และใช้งานทางอ้อมเท่านั้นปัญหานี้ยังกล่าวถึงในโพสต์อื่นด้วยดังนั้นฉันคิดว่ามันคุ้มค่า เพื่อโพสต์การค้นพบเล็ก ๆ ของฉันที่นี่

หวังว่านี่จะเป็นประโยชน์กับใครบางคนในอนาคต


0

ฉันประสบปัญหาเดียวกันเมื่อปิดตัวควบคุมมุมมองเนื้อหา

แอปของฉันมีตัวควบคุมมุมมองหลักนี้ที่แสดงตัวควบคุมมุมมองเด็ก (นำเสนอ vc) แบบมอดูล จากนั้นเมื่อมีการแตะ subview ใน childVC จะแสดง vc อื่น (ซึ่งฉันเรียกว่าตัวควบคุมมุมมองเนื้อหา (นำเสนอ vc))

ปัญหาของฉันคือเมื่อปิด contentVC (ปัจจุบันคือ vc การนำเสนอ) ควรไปที่ VC ลูก (ตอนนี้คือ VC ที่นำเสนอ) แต่ทันทีที่การเปลี่ยนแปลงที่กำหนดเองของฉันเสร็จสิ้น childVC ก็หายไปทันทีโดยแสดง VC หลัก

สิ่งที่ฉันทำเพื่อแก้ปัญหานี้คือ

  1. เปลี่ยน.modalPresentationStyleของ childVC นำเสนอโดย parentVC จากค่าเริ่มต้นที่จะ.automatic.fullscreen
  2. จากนั้นเปลี่ยน.modalPresentationStyleของ contentVC .fullscreenเป็นเช่นกัน

วิธีนี้ช่วยแก้ปัญหาได้ แต่จะไม่แสดง VC ลูกของคุณเป็นการ์ดสไตล์ชีทที่ด้านบนของ parentVC (เมื่อใช้.overCurrentContextหรืออัตโนมัติ) ซึ่งเป็นของใหม่ใน iOS 13

อยากทราบว่ามีวิธีแก้ปัญหาใดบ้างที่จะคงรูปแบบการ์ดไว้สำหรับ childVC เมื่อนำเสนอโดยผู้ปกครอง


-3

เพิ่มตัวควบคุมมุมมองเป็นลูกของตัวควบคุมมุมมองอื่น

[self addChildViewController:childViewController];                 

ตรวจสอบและแจ้งให้เราทราบ


ฉันไม่เข้าใจคุณสามารถอธิบายโดยใช้การเข้ารหัสได้หรือไม่?
NiravPatel


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