Bộ điều khiển xem truy cập container từ cha mẹ iOS


203

trong iOS6 tôi nhận thấy Chế độ xem Container mới nhưng không chắc chắn cách truy cập bộ điều khiển của nó từ chế độ xem chứa.

Kịch bản:

thí dụ

Tôi muốn truy cập các nhãn trong Trình điều khiển chế độ xem cảnh báo từ trình điều khiển chế độ xem chứa chế độ xem vùng chứa.

Có một sự khác biệt giữa chúng, tôi có thể sử dụng nó không?


giải thích đầy đủ ở đây, cho các chế độ xem hiện đại của container: stackoverflow.com/a/23403979/294884
Fattie

Câu trả lời:


362

Có, bạn có thể sử dụng segue để truy cập vào trình điều khiển khung nhìn con (và chế độ xem và xem trước của nó). Cung cấp cho segue một mã định danh (chẳng hạn như alertview_embed), bằng cách sử dụng trình kiểm tra các thuộc tính trong Storyboard. Sau đó, có bộ điều khiển khung nhìn cha (khung chứa khung nhìn container) thực hiện một phương thức như sau:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
   NSString * segueName = segue.identifier;
   if ([segueName isEqualToString: @"alertview_embed"]) {
       AlertViewController * childViewController = (AlertViewController *) [segue destinationViewController];
       AlertView * alertView = childViewController.view;
       // do something with the AlertView's subviews here...
   }
}

1
chúng ta không phân biệt? Am i thiếu cái gì ở đây...?
Adam Chờ đợi

25
có, có một phân tách nhúng xảy ra khi bộ điều khiển khung nhìn thứ hai được tạo thành con của bộ điều khiển khung nhìn thứ nhất. readyForSegue: được gọi ngay trước khi điều này xảy ra. bạn có thể sử dụng cơ hội này để truyền dữ liệu cho trẻ hoặc lưu trữ tài liệu tham khảo cho trẻ để sử dụng sau. xem thêm developer.apple.com/l
Peter E

1
À đúng rồi, 'bộ điều khiển chế độ xem thứ hai có phải là con của bộ điều khiển chế độ xem thứ nhất' khi chế độ xem tải không? Điều này có ý nghĩa hơn bây giờ, cảm ơn. Bây giờ tôi không tham gia dự án của mình nhưng sẽ kiểm tra sau
Adam Waite

1
chính xác, nó được gọi trước viewDidLoad. Vào thời điểm viewDidLoad đạt được, cha mẹ và con đã được kết nối và [self childViewControllers] trong cha mẹ sẽ trả về một mảng của tất cả các bộ điều khiển con (xem câu trả lời của ndelmar bên dưới).
Peter E

2
Tôi sẽ thêm một cảnh báo vào giải pháp được đề xuất: rất cẩn thận khi truy cập thuộc tính khung nhìn của trình điều khiển khung nhìn đích (con): trong một số trường hợp, điều này sẽ khiến viewDidLoad của nó được gọi ở đó và sau đó. Tôi sẽ khuyên bạn nên thiết lập bất kỳ dữ liệu tách biệt cần thiết nào trước đó để viewDidLoad có thể bắn an toàn.
Luôn luôn học tập vào

56

Bạn có thể làm điều đó một cách đơn giản với self.childViewControllers.lastObject(giả sử bạn chỉ có một con, nếu không thì sử dụng objectAtIndex:).


1
@RaphaelOliveira, không nhất thiết phải như vậy. Nếu bạn có nhiều childControllers trong một chế độ xem, thì NÀY sẽ là phương pháp ưa thích. Nó cho phép bạn phối hợp nhiều container cùng một lúc. readyForSegue chỉ có tham chiếu đến cá thể bộ điều khiển con duy nhất mà nó hoạt động.
Fydo

2
@Fydo, và vấn đề với việc xử lý tất cả các container trên 'chuẩn bị cho segue' là gì?
Lay González

1
Điều gì sẽ xảy ra nếu (khủng khiếp!) Bạn quyết định chuyển từ bảng phân cảnh hoặc không sử dụng các chuỗi, v.v. Sau đó, bạn phải đào mã để thực hiện các thay đổi, v.v.
Tom Andersen

2
Đây là cách tiếp cận thông thường của tôi, nhưng bây giờ nó gặp sự cố vì tôi đang truy cập vào childViewControllers"quá sớm"
Mazyod

24

cho lập trình Swift

bạn có thể viết như thế này

var containerViewController: ExampleViewController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // you can set this name in 'segue.embed' in storyboard
    if segue.identifier == "checkinPopupIdentifierInStoryBoard" {
        let connectContainerViewController = segue.destinationViewController as ExampleViewController
        containerViewController = connectContainerViewController
    }
}

Việc sử dụng cho dấu hỏi sau segueName trên câu lệnh if là gì? "nếu segueName?"
Reverend

18

Cách prepareForSeguetiếp cận hoạt động, nhưng nó dựa vào chuỗi ma thuật định danh segue. Có lẽ có một cách tốt hơn.

Nếu bạn biết lớp VC mà bạn theo đuổi, bạn có thể thực hiện việc này rất gọn gàng với một thuộc tính được tính toán:

var camperVan: CamperVanViewController? {
  return childViewControllers.flatMap({ $0 as? CamperVanViewController }).first
  // This works because `flatMap` removes nils
}

Điều này dựa vào childViewControllers. Mặc dù tôi đồng ý rằng việc dựa vào người đầu tiên có thể rất mong manh, việc đặt tên cho lớp bạn tìm kiếm khiến việc này có vẻ khá vững chắc.


3
return childViewControllers.filter { $0 is CamperVanViewController }.firsttrong một lớp lót
Adam Waite

1
Kể từ khi tôi thực hiện childViewControllers.flatMap({ $0 as? CamperVanViewController }).firstđiều mà tôi nghĩ là tốt hơn một chút, vì nó diễn xuất và thoát khỏi mọi nils.
SimplGy

Đây là một giải pháp thực sự tốt nếu bạn muốn truy cập bộ điều khiển xem đó nhiều lần
Gabriel Goncalves

điều này là vô vọng - không có lý do cụ thể nào bạn có thể chỉ có một trong những lớp cụ thể đó. đó chính xác là lý do tại sao định danh tồn tại. chỉ cần làm theo công thức tiêu chuẩn ... stackoverflow.com/a/23403979/294884
Fattie

không chỉ lọc để lấy phần tử đầu tiên. Chỉ cần sử dụng first(where:). childViewControllers.first(where: { $0 is CamperVanViewController })
Alexander - Tái lập Monica

9

Câu trả lời được cập nhật cho Swift 3, sử dụng thuộc tính được tính toán:

var jobSummaryViewController: JobSummaryViewController {
    get {
        let ctrl = childViewControllers.first(where: { $0 is JobSummaryViewController })
        return ctrl as! JobSummaryViewController
    }
}

Điều này chỉ lặp lại danh sách trẻ em cho đến khi nó đạt đến trận đấu đầu tiên.


8

self.childViewControllers có liên quan hơn khi bạn cần sự kiểm soát từ cha mẹ. Ví dụ: nếu bộ điều khiển con là chế độ xem bảng và bạn muốn tải lại mạnh mẽ hoặc thay đổi thuộc tính thông qua một lần nhấn nút hoặc bất kỳ sự kiện nào khác trên Trình điều khiển xem cha mẹ, bạn có thể thực hiện bằng cách truy cập vào thể hiện của ChildViewContoder và không thông qua chuẩn bịForForSegue. Cả hai đều có ứng dụng của họ theo những cách khác nhau.


2

Có một cách khác bằng cách sử dụng câu lệnh chuyển đổi của Swift trên loại trình điều khiển chế độ xem:

override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
  switch segue.destination
  {
    case let aViewController as AViewController:
      self.aViewController = aViewController
    case let bViewController as BViewController:
      self.bViewController = bViewController
    default:
      return
  }
}

1

Tôi sử dụng Mã như:

- (IBAction)showCartItems:(id)sender{ 
  ListOfCartItemsViewController *listOfItemsVC=[self.storyboard instantiateViewControllerWithIdentifier:@"ListOfCartItemsViewController"];
  [self addChildViewController:listOfItemsVC];
 }

1

Trong trường hợp ai đó đang tìm kiếm Swift 3.0 ,

viewControll1 , viewControll2 và sau đó sẽ có thể truy cập được.

let viewController1 : OneViewController!
let viewController2 : TwoViewController!

// Safety handling of optional String
if let identifier: String = segue.identifier {

    switch identifier {

    case "segueName1":
        viewController1 = segue.destination as! OneViewController
        break

    case "segueName2":
        viewController2 = segue.destination as! TwoViewController
        break

    // ... More cases can be inserted here ...

    default:
        // A new segue is added in the storyboard but not yet including in this switch
        print("A case missing for segue identifier: \(identifier)")
        break
    }

} else {
    // Either the segue or the identifier is inaccessible 
    print("WARNING: identifier in segue is not accessible")
}

1

Với chung chung bạn có thể làm một số điều ngọt ngào. Đây là một phần mở rộng cho Array:

extension Array {
    func firstMatchingType<Type>() -> Type? {
        return first(where: { $0 is Type }) as? Type
    }
}

Sau đó, bạn có thể làm điều này trong viewControll của bạn:

var viewControllerInContainer: YourViewControllerClass? {
    return childViewControllers.firstMatchingType()!
}

0

bạn có thể viết như thế này

- (IBAction)showDetail:(UIButton *)sender {  
            DetailViewController *detailVc = [self.childViewControllers firstObject];  
        detailVc.lable.text = sender.titleLabel.text;  
    }  
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.