ไปที่ UIViewController จาก UIView


185

มีวิธีในการรับจาก a UIViewไปยังUIViewControllerหรือไม่? ฉันรู้ว่าคุณจะได้รับจากผ่านUIViewControllerของมันแต่ฉันสงสัยว่ามีการอ้างอิงย้อนกลับ?UIView[self view]

คำตอบ:


46

เนื่องจากนี่เป็นคำตอบที่ได้รับการยอมรับมาเป็นเวลานานฉันรู้สึกว่าฉันต้องแก้ไขมันด้วยคำตอบที่ดีกว่า

ความคิดเห็นบางอย่างเกี่ยวกับความต้องการ:

  • มุมมองของคุณไม่จำเป็นต้องเข้าถึงตัวควบคุมมุมมองโดยตรง
  • มุมมองควรเป็นอิสระจากตัวควบคุมมุมมองและสามารถทำงานในบริบทต่าง ๆ ได้
  • หากคุณต้องการมุมมองในการติดต่อกับผู้ควบคุมมุมมองวิธีที่แนะนำและสิ่งที่ Apple ทำใน Cocoa คือการใช้รูปแบบของผู้ได้รับมอบหมาย

ตัวอย่างของวิธีการใช้ดังต่อไปนี้:

@protocol MyViewDelegate < NSObject >

- (void)viewActionHappened;

@end

@interface MyView : UIView

@property (nonatomic, assign) MyViewDelegate delegate;

@end

@interface MyViewController < MyViewDelegate >

@end

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

คำตอบเดิมของฉันมีดังนี้: ฉันไม่แนะนำสิ่งนี้, ไม่ใช่คำตอบที่เหลือซึ่งการเข้าถึงตัวควบคุมมุมมองโดยตรง

ไม่มีวิธีการติดตั้งในตัว ในขณะที่คุณจะได้รับรอบโดยการเพิ่มIBOutletในUIViewและการเชื่อมต่อเหล่านี้ใน Interface Builder นี้ไม่แนะนำ มุมมองไม่ควรรู้เกี่ยวกับตัวควบคุมมุมมอง คุณควรทำตามที่ @Phil M แนะนำและสร้างโปรโตคอลที่จะใช้เป็นผู้รับมอบสิทธิ์


26
นั่นเป็นคำแนะนำที่แย่มาก คุณไม่ควรอ้างอิงตัวควบคุมมุมมองจากมุมมอง
Philippe Leybaert

6
@ MattiPasquale: ใช่มันออกแบบไม่ดี
Philippe Leybaert

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

3
@PhilippeLeybaert ตัวอย่างโครงการของ Apple มีแนวโน้มที่จะแสดงการใช้งานคุณสมบัติ API เฉพาะ หลายคนที่ฉันเคยพูดถึงการเสียสละการออกแบบที่ดีหรือปรับขนาดได้เพื่อให้การสาธิตที่กระชับของหัวข้อ ฉันใช้เวลานานในการตระหนักถึงสิ่งนี้และในขณะที่มันทำให้รู้สึกฉันพบว่ามันโชคร้าย ฉันคิดว่านักพัฒนาจำนวนมากใช้โครงการในทางปฏิบัติเหล่านี้เป็นแนวทางของ Apple ในการออกแบบการออกแบบที่ดีที่สุดซึ่งฉันค่อนข้างแน่ใจว่าพวกเขาไม่ได้
Benjohn

11
ทั้งหมดนี้เกี่ยวกับ "คุณไม่ควรทำเช่นนี้" ก็แค่นั้น หากมุมมองต้องการทราบเกี่ยวกับตัวควบคุมมุมมองมุมมองนั้นขึ้นอยู่กับโปรแกรมเมอร์ที่จะตัดสินใจ ระยะเวลา
Daniel Kanaan

203

จากการใช้ตัวอย่างที่โพสต์โดย Brock ฉันได้ทำการแก้ไขเพื่อให้เป็นหมวดหมู่ของ UIView แทน UIViewController และทำให้มันซ้ำเพื่อให้มุมมองย่อยใด ๆ (หวังว่า) จะค้นหา UIViewController หลัก

@interface UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController;
- (id) traverseResponderChainForUIViewController;
@end

@implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
    // convenience function for casting and to "mask" the recursive function
    return (UIViewController *)[self traverseResponderChainForUIViewController];
}

- (id) traverseResponderChainForUIViewController {
    id nextResponder = [self nextResponder];
    if ([nextResponder isKindOfClass:[UIViewController class]]) {
        return nextResponder;
    } else if ([nextResponder isKindOfClass:[UIView class]]) {
        return [nextResponder traverseResponderChainForUIViewController];
    } else {
        return nil;
    }
}
@end

หากต้องการใช้รหัสนี้ให้เพิ่มลงในไฟล์คลาสใหม่ (ฉันชื่อ mine "UIKitCategories") และลบข้อมูลคลาส ... คัดลอก @interface ไปยังส่วนหัวและ @implementation ลงในไฟล์. m จากนั้นในโครงการของคุณ #import "UIKitCategories.h" และใช้ภายในรหัส UIView:

// from a UIView subclass... returns nil if UIViewController not available
UIViewController * myController = [self firstAvailableUIViewController];

47
และเหตุผลหนึ่งที่ทำให้คุณต้องอนุญาตให้ UIView ทราบถึง UIViewController ของมันคือเมื่อคุณมีคลาสย่อยของ UIView ที่กำหนดเองซึ่งจำเป็นต้องผลักดันมุมมอง / กล่องโต้ตอบเป็นกิริยาช่วย
Phil M

ยอดเยี่ยมฉันต้องเข้าถึง ViewController ของฉันเพื่อแสดงป๊อปอัพที่กำหนดเองซึ่งสร้างขึ้นโดยมุม
มองย่อย

2
นี่ไม่ใช่การปฏิบัติที่ไม่ดีสำหรับ UIView ที่จะผลักดันมุมมองที่เป็นกิริยาช่วยใช่หรือไม่ ฉันกำลังทำสิ่งนี้อยู่ แต่ฉันรู้สึกว่ามันไม่ใช่สิ่งที่ถูกต้องที่จะทำ ..
Van Du Tran

6
Phil มุมมองที่กำหนดเองของคุณควรเรียกวิธีการมอบสิทธิ์ซึ่งตัวควบคุมมุมมองรับฟังแล้วกดจากที่นั่น
malhal

9
ฉันรักคำถามที่มีจำนวนมากดังนั้น "นักปราชญ์" นักวิชาการคำตอบที่ได้รับการยอมรับมีเพียงไม่กี่ข้อและคำตอบที่สองซึ่งใช้งานได้จริงสกปรกและขัดกับกฎด้วยคะแนนสิบเท่า :-)
hariseldon78

114

UIViewเป็น subclass UIResponderของ UIResponderออกวางวิธีการที่มีการดำเนินการที่ผลตอบแทน-nextResponder แทนที่วิธีการนี้เป็นเอกสารใน(ด้วยเหตุผลบางอย่างแทนใน) เป็นดังนี้: ถ้ามุมมองที่มีตัวควบคุมมุมมองของมันจะถูกส่งกลับโดย หากไม่มีตัวควบคุมมุมมองวิธีการจะส่งคืนผู้ควบคุมnilUIViewUIResponderUIView-nextResponder

เพิ่มสิ่งนี้ลงในโครงการของคุณและคุณพร้อมแล้ว

@interface UIView (APIFix)
- (UIViewController *)viewController;
@end

@implementation UIView (APIFix)

- (UIViewController *)viewController {
    if ([self.nextResponder isKindOfClass:UIViewController.class])
        return (UIViewController *)self.nextResponder;
    else
        return nil;
}
@end

ขณะนี้UIViewมีวิธีการทำงานสำหรับส่งคืนตัวควบคุมมุมมอง


5
นี้จะทำงานถ้ามีอะไรในห่วงโซ่การตอบกลับระหว่างที่ได้รับและUIView UIViewControllerคำตอบของ Phil M ที่มีการเรียกซ้ำเป็นวิธีที่จะไป
Olivier

33

ฉันขอแนะนำวิธีที่มีน้ำหนักเบากว่าสำหรับการสำรวจห่วงโซ่การตอบกลับที่สมบูรณ์โดยไม่ต้องเพิ่มหมวดหมู่ใน UIView:

@implementation MyUIViewSubclass

- (UIViewController *)viewController {
    UIResponder *responder = self;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

@end

22

เมื่อรวมหลายคำตอบที่ได้รับไปแล้วฉันก็ส่งไปพร้อม ๆ กับการติดตั้งของฉัน:

@implementation UIView (AppNameAdditions)

- (UIViewController *)appName_viewController {
    /// Finds the view's view controller.

    // Take the view controller class object here and avoid sending the same message iteratively unnecessarily.
    Class vcc = [UIViewController class];

    // Traverse responder chain. Return first found view controller, which will be the view's view controller.
    UIResponder *responder = self;
    while ((responder = [responder nextResponder]))
        if ([responder isKindOfClass: vcc])
            return (UIViewController *)responder;

    // If the view controller isn't found, return nil.
    return nil;
}

@end

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

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


1
นี่คือคำตอบที่ดีที่สุด ไม่จำเป็นต้องมีการสอบถามซ้ำรุ่นนี้ได้รับการปรับปรุงให้ดีที่สุด
zeroimpl

12

แม้ว่าจะสามารถแก้ไขได้ในทางเทคนิคตามที่pgbแนะนำ แต่ IMHO นี่เป็นข้อบกพร่องในการออกแบบ มุมมองที่ไม่จำเป็นต้องระวังของตัวควบคุม


ฉันไม่แน่ใจว่า viewController สามารถบอกได้อย่างไรว่าหนึ่งในมุมมองนั้นกำลังจะหายไปและต้องเรียกวิธีการอย่างใดอย่างหนึ่งของ viewXXXAppear / viewXXXXDisappear
mahboudz

2
นี่คือแนวคิดเบื้องหลังรูปแบบการสังเกตการณ์ ผู้สังเกตการณ์ (มุมมองในกรณีนี้) ไม่ควรรับรู้ถึงผู้สังเกตการณ์โดยตรง นักสังเกตการณ์เท่านั้นที่ควรจะได้รับการเรียกกลับที่พวกเขากำลังสนใจในการ.
Ushox

12

ผมปรับเปลี่ยนdeคำตอบดังนั้นฉันสามารถผ่านมุมมองใด ๆ ปุ่มป้ายอื่น ๆ UIViewControllerจะได้รับมันของผู้ปกครอง นี่คือรหัสของฉัน

+(UIViewController *)viewController:(id)view {
    UIResponder *responder = view;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

แก้ไข Swift 3 Version

class func viewController(_ view: UIView) -> UIViewController {
        var responder: UIResponder? = view
        while !(responder is UIViewController) {
            responder = responder?.next
            if nil == responder {
                break
            }
        }
        return (responder as? UIViewController)!
    }

แก้ไข 2: - Extention สวิฟท์

extension UIView
{
    //Get Parent View Controller from any view
    func parentViewController() -> UIViewController {
        var responder: UIResponder? = self
        while !(responder is UIViewController) {
            responder = responder?.next
            if nil == responder {
                break
            }
        }
        return (responder as? UIViewController)!
    }
}

7

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

    [[[[self window] rootViewController] navigationController] pushViewController:newController animated:YES];

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

-(void) applicationDidFinishLaunching:(UIApplication *)application {
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    RootViewController *controller = [[YourRootViewController] alloc] init];
    [window setRootViewController: controller];
    navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
    [controller release];
    [window addSubview:[[self navigationController] view]];
    [window makeKeyAndVisible];
}

สำหรับฉันแล้วดูเหมือนว่าตามเอกสารของ Appleเนื่องจาก[[self navigationController] view]เป็นมุมมอง "หลัก" (ย่อย) ของหน้าต่างrootViewControllerคุณสมบัติของหน้าต่างจะต้องถูกตั้งค่าให้navigationControllerควบคุมมุมมอง "หลัก" โดยทันที
adubr

6

ในขณะที่คำตอบเหล่านี้ถูกต้องทางเทคนิครวมถึง Ushox ฉันคิดว่าวิธีที่ได้รับการอนุมัติคือการใช้โปรโตคอลใหม่หรือนำมาใช้ใหม่ที่มีอยู่ โปรโตคอลป้องกันผู้สังเกตการณ์จากการสังเกตเช่นการใส่ช่องใส่อีเมลระหว่างพวกเขา ผลคือนั่นคือสิ่งที่กาเบรียลทำผ่านการเรียกใช้เมธอด pushViewController มุมมอง "รู้" ว่าเป็นโปรโตคอลที่เหมาะสมที่จะขอให้สุภาพนำทางของคุณเพื่อผลักดันมุมมองเนื่องจาก viewController เป็นไปตามโปรโตคอลนำทางController ในขณะที่คุณสามารถสร้างโปรโตคอลของคุณเองเพียงแค่ใช้ตัวอย่างของ Gabriel และการใช้โปรโตคอล UINavigationController อีกครั้งก็ใช้ได้


6

ฉันสะดุดกับสถานการณ์ที่ฉันมีส่วนประกอบขนาดเล็กที่ฉันต้องการนำมาใช้ใหม่และเพิ่มโค้ดบางส่วนในมุมมองที่นำกลับมาใช้ใหม่ได้เอง (มันไม่ได้เป็นอะไรมากไปกว่าปุ่มที่เปิดPopoverController)

ในขณะที่วิธีนี้ใช้งานได้ดีใน iPad ( UIPopoverControllerของขวัญนั้นไม่จำเป็นต้องอ้างอิงถึง a UIViewController) การใช้รหัสเดียวกันในการทำงานหมายถึงการอ้างอิงpresentViewControllerจากคุณในUIViewControllerทันที ค่อนข้างไม่สอดคล้องใช่มั้ย

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

ไม่ว่าจะด้วยวิธีใดนี่คือโซลูชันที่รวดเร็วซึ่งจะเพิ่มคุณสมบัติใหม่ให้กับ UIView ใด ๆ :

extension UIView {

    var viewController: UIViewController? {

        var responder: UIResponder? = self

        while responder != nil {

            if let responder = responder as? UIViewController {
                return responder
            }
            responder = responder?.nextResponder()
        }
        return nil
    }
}

5

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

//.h

@property (nonatomic, readonly) UIViewController * viewController;

//.m

- (UIViewController *)viewController
{
    for (UIResponder * nextResponder = self.nextResponder;
         nextResponder;
         nextResponder = nextResponder.nextResponder)
    {
        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController *)nextResponder;
    }

    // Not found
    NSLog(@"%@ doesn't seem to have a viewController". self);
    return nil;
}

4

วิธีที่ง่ายที่สุดในขณะที่วนรอบเพื่อค้นหา viewController

-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;

    do
    {
        nextResponder = [nextResponder nextResponder];

        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder != nil);

    return nil;
}

4

สวิฟท์ 4

(กระชับกว่าคำตอบอื่น ๆ )

fileprivate extension UIView {

  var firstViewController: UIViewController? {
    let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
    return firstViewController as? UIViewController
  }

}

กรณีการใช้งานของฉันที่ฉันจำเป็นต้องเข้าถึงมุมมองแรกUIViewController: ฉันมีวัตถุที่ล้อมรอบAVPlayer/ AVPlayerViewControllerและฉันต้องการให้ง่ายshow(in view: UIView)วิธีการที่จะฝังลงในAVPlayerViewController viewเพื่อที่ฉันจะต้องเข้าถึง'sviewUIViewController


3

สิ่งนี้ไม่ได้ตอบคำถามโดยตรง แต่ให้ตั้งสมมติฐานเกี่ยวกับเจตนาของคำถาม

ถ้าคุณมีมุมมองและในมุมมองนั้นคุณจำเป็นต้องเรียกใช้เมธอดบนวัตถุอื่นเช่นตัวควบคุมมุมมองคุณสามารถใช้ NSNotificationCenter แทน

ขั้นแรกสร้างสตริงการแจ้งเตือนของคุณในไฟล์ส่วนหัว

#define SLCopyStringNotification @"ShaoloCopyStringNotification"

ในมุมมองการโทรของคุณหมายเหตุชื่อ:

- (IBAction) copyString:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:SLCopyStringNotification object:nil];
}

จากนั้นในตัวควบคุมมุมมองของคุณคุณเพิ่มผู้สังเกตการณ์ ฉันทำสิ่งนี้ใน viewDidLoad

- (void)viewDidLoad
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(copyString:)
                                                 name:SLCopyStringNotification
                                               object:nil];
}

ตอนนี้ (ในตัวควบคุมมุมมองเดียวกัน) ใช้วิธีการของคุณ copyString: ตามที่ปรากฎใน @selector ด้านบน

- (IBAction) copyString:(id)sender
{
    CalculatorResult* result = (CalculatorResult*)[[PercentCalculator sharedInstance].arrayTableDS objectAtIndex:([self.viewTableResults indexPathForSelectedRow].row)];
    UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
    [gpBoard setString:result.stringResult];
}

ฉันไม่ได้บอกว่านี่เป็นวิธีที่ถูกต้องในการทำสิ่งนี้ดูเหมือนว่าจะสะอาดกว่าการวิ่งขึ้นห่วงโซ่การตอบกลับแรก ฉันใช้รหัสนี้เพื่อใช้ UIMenuController บน UITableView และส่งเหตุการณ์กลับไปที่ UIViewController เพื่อให้ฉันสามารถทำอะไรกับข้อมูลได้


3

แน่นอนว่ามันเป็นความคิดที่ไม่ดีและการออกแบบที่ไม่ถูกต้อง แต่ฉันแน่ใจว่าเราทุกคนสามารถเพลิดเพลินไปกับโซลูชั่น Swift ของคำตอบที่ดีที่สุดที่เสนอโดย @Phil_M:

static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
    func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
        if let nextResponder = responder.nextResponder() {
            if let nextResp = nextResponder as? UIViewController {
                return nextResp
            } else {
                return traverseResponderChainForUIViewController(nextResponder)
            }
        }
        return nil
    }

    return traverseResponderChainForUIViewController(responder)
}

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

if let viewController = MyUtilityClass.firstAvailableUIViewController(self) {}

เครดิตทั้งหมดถึง @Phil_M


3

บางทีฉันอาจจะมาสาย แต่ในสถานการณ์นี้ฉันไม่ชอบหมวดหมู่ (มลพิษ) ฉันชอบวิธีนี้:

#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})

3

ทางออกที่รวดเร็ว

extension UIView {
    var parentViewController: UIViewController? {
        for responder in sequence(first: self, next: { $0.next }) {
            if let viewController = responder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

ฉันชอบคำตอบนี้ ฉันไม่แน่ใจว่ามันรวดเร็วกว่านี้อีกแล้ว Swift นั้นเป็นอีกภาษาหนึ่งที่ไม่สามารถตัดสินใจได้ว่าภาษาใดในหลายกระบวนทัศน์ที่ต้องการแต่งงาน ในกรณีนี้คุณมีการสาธิตวิธีการที่ใช้งานได้ดีขึ้น ( sequenceบิต) ดังนั้นถ้า "swifty" หมายถึง "การทำงานได้มากกว่า" ฉันก็เดาว่ามันจะเพิ่มขึ้นอย่างรวดเร็ว
Travis Griggs

2

อัปเดตเวอร์ชันสำหรับ swift 4: ขอบคุณสำหรับ @Phil_M และ @ paul-slm

static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
    func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
        if let nextResponder = responder.next {
            if let nextResp = nextResponder as? UIViewController {
                return nextResp
            } else {
                return traverseResponderChainForUIViewController(responder: nextResponder)
            }
        }
        return nil
    }

    return traverseResponderChainForUIViewController(responder: responder)
}

2

รุ่น Swift 4

extension UIView {
var parentViewController: UIViewController? {
    var parentResponder: UIResponder? = self
    while parentResponder != nil {
        parentResponder = parentResponder!.next
        if let viewController = parentResponder as? UIViewController {
            return viewController
        }
    }
    return nil
}

ตัวอย่างการใช้งาน

 if let parent = self.view.parentViewController{

 }

2

สองวิธีนับตั้งแต่Swift 5.2 :

  • เพิ่มเติมเกี่ยวกับหน้าที่การใช้งาน
  • ไม่จำเป็นต้องใช้returnคำหลักตอนนี้🤓

โซลูชันที่ 1:

extension UIView {
    var parentViewController: UIViewController? {
        sequence(first: self) { $0.next }
            .first(where: { $0 is UIViewController })
            .flatMap { $0 as? UIViewController }
    }
}

โซลูชันที่ 2:

extension UIView {
    var parentViewController: UIViewController? {
        sequence(first: self) { $0.next }
            .compactMap{ $0 as? UIViewController }
            .first
    }
}
  • การแก้ปัญหานี้ต้องการการวนซ้ำผ่านผู้ตอบคำถามแต่ละคนก่อนดังนั้นจึงอาจไม่ใช่ผู้มีประสิทธิภาพสูงสุด

1

คำตอบของฟิล:

ในบรรทัด: id nextResponder = [self nextResponder]; ถ้าตัวเอง (UIView) ไม่ได้เป็น subview ของมุมมองของ ViewController ถ้าคุณรู้ว่าลำดับขั้นของตนเอง (UIView) คุณสามารถใช้เพิ่มเติม: id nextResponder = [[self superview] nextResponder];...


0

โซลูชันของฉันอาจถูกพิจารณาว่าเป็นการหลอกลวง แต่ฉันมีสถานการณ์คล้ายกันกับ mayoneez (ฉันต้องการสลับมุมมองเพื่อตอบสนองต่อท่าทางใน EAGLView) และฉันได้ตัวควบคุมมุมมองของ EAGL ด้วยวิธีนี้:

EAGLViewController *vc = ((EAGLAppDelegate*)[[UIApplication sharedApplication] delegate]).viewController;

2
EAGLViewController *vc = [(EAGLAppDelegate *)[UIApplication sharedApplication].delegate viewController];กรุณาเขียนรหัสของคุณดังนี้
โจนาธานสเตอร์ลิง

1
ปัญหาไม่ได้อยู่กับไวยากรณ์จุด แต่มีประเภท ใน Objective C เพื่อประกาศวัตถุที่คุณเขียนClassName *objectด้วยเครื่องหมายดอกจัน
adubr

อ๊ะ ... นั่นคือสิ่งที่ฉันมีจริงๆ แต่วิดเจ็ต StackOverflow HTML ดูเหมือนสิ่งที่มันคิดว่าเครื่องหมายดอกจันหมายถึงตัวเอียง ... ฉันเปลี่ยนมันเป็นบล็อกรหัสตอนนี้มันแสดงได้อย่างถูกต้อง ขอบคุณ!
gulchrider

0

ฉันคิดว่ามีกรณีเมื่อผู้สังเกตการณ์ต้องการแจ้งผู้สังเกตการณ์

ฉันเห็นปัญหาที่คล้ายกันซึ่ง UIView ใน UIViewController ตอบสนองต่อสถานการณ์และต้องบอกตัวควบคุมมุมมองหลักเพื่อซ่อนปุ่มย้อนกลับก่อนแล้วจึงบอกตัวควบคุมมุมมองหลักที่ต้องการให้ปรากฏตัวออกจากสแต็ก

ฉันได้ลองทำสิ่งนี้กับผู้ร่วมประชุมที่ไม่ประสบความสำเร็จ

ฉันไม่เข้าใจว่าทำไมจึงเป็นความคิดที่ไม่ดี


0

อีกวิธีง่าย ๆ คือการมีคลาสมุมมองของคุณเองและเพิ่มคุณสมบัติของตัวควบคุมมุมมองในมุมมองคลาส โดยปกติแล้วตัวควบคุมมุมมองจะสร้างมุมมองและเป็นที่ที่ตัวควบคุมสามารถตั้งค่าตัวเองกับคุณสมบัติ โดยพื้นฐานแล้วแทนที่จะค้นหารอบ ๆ (ด้วยการแฮ็กเล็กน้อย) สำหรับตัวควบคุมโดยให้ตัวควบคุมตั้งค่าตัวเองไปยังมุมมอง - นี่เป็นเรื่องง่าย แต่ก็สมเหตุสมผลเนื่องจากเป็นตัวควบคุมที่ "ควบคุม" มุมมอง


0

หากคุณจะไม่อัปโหลดไปที่ App Store คุณสามารถใช้วิธีการส่วนตัวของ UIView

@interface UIView(Private)
- (UIViewController *)_viewControllerForAncestor;
@end

// Later in the code
UIViewController *vc = [myView _viewControllerForAncestor];

0
var parentViewController: UIViewController? {
    let s = sequence(first: self) { $0.next }
    return s.compactMap { $0 as? UIViewController }.first
}

แม้ว่ารหัสนี้อาจตอบคำถาม แต่คำตอบที่ดีควรอธิบายว่ารหัสทำอะไรและวิธีแก้ปัญหา
BDL

0

เพื่อให้ตัวควบคุมของมุมมองที่กำหนดหนึ่งสามารถใช้ UIFirstResponder ห่วงโซ่

customView.target(forAction: Selector("viewDidLoad"), withSender: nil)

-1

หาก rootViewController ของคุณคือ UINavigationViewController ซึ่งถูกตั้งค่าในคลาส AppDelegate

    + (UIViewController *) getNearestViewController:(Class) c {
NSArray *arrVc = [[[[UIApplication sharedApplication] keyWindow] rootViewController] childViewControllers];

for (UIViewController *v in arrVc)
{
    if ([v isKindOfClass:c])
    {
        return v;
    }
}

return nil;}

โดยที่ c ต้องการคลาสตัวควบคุมมุมมอง

การใช้:

     RequiredViewController* rvc = [Utilities getNearestViewController:[RequiredViewController class]];

-5

ไม่มีทาง.

สิ่งที่ฉันทำคือส่งตัวชี้ UIViewController ไปยัง UIView (หรือการสืบทอดที่เหมาะสม) ฉันขอโทษที่ฉันไม่สามารถช่วยปัญหา IB ได้เพราะฉันไม่เชื่อใน IB

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


10
หมายความว่าอะไร - "ฉันไม่เชื่อใน IB"? ฉันได้เปิดตัวมันมีอยู่อย่างแน่นอน
marcc

5
คุณต้องเข้าใจถึงความสนุกและความเป็นนามธรรมโดยเฉพาะอย่างยิ่งในเรื่องภาษาอังกฤษ หมายความว่าฉันไม่ชอบ
John Smith

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