bác bỏModalViewController VÀ chuyển dữ liệu trở lại


84

Tôi có hai bộ điều khiển chế độ xem, firstViewControllersecondViewController . Tôi đang sử dụng mã này để chuyển sangViewController thứ hai của mình (tôi cũng đang chuyển một chuỗi vào nó):

secondViewController *second = [[secondViewController alloc] initWithNibName:nil bundle:nil];

second.myString = @"This text is passed from firstViewController!";

second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

[self presentModalViewController:second animated:YES];

[second release];

Sau đó, tôi sử dụng mã này trong secondViewController để chuyển trở lạiViewController đầu tiên:

[self dismissModalViewControllerAnimated:YES];

Tất cả điều này hoạt động tốt. Câu hỏi của tôi là, tôi sẽ chuyển dữ liệu vào firstViewController như thế nào? Tôi muốn chuyển một chuỗi khác vàoViewController đầu tiên từViewController thứ hai.

Câu trả lời:


142

Bạn cần sử dụng các giao thức ủy quyền ... Dưới đây là cách thực hiện:

Khai báo một giao thức trong tệp tiêu đề thứ haiViewController của bạn. Nó sẽ giống như thế này:

#import <UIKit/UIKit.h>

@protocol SecondDelegate <NSObject>
-(void)secondViewControllerDismissed:(NSString *)stringForFirst
@end


@interface SecondViewController : UIViewController
{
    id myDelegate;  
}

@property (nonatomic, assign) id<SecondDelegate>    myDelegate;

Đừng quên tổng hợp myDelegate trong tệp triển khai (SecondViewController.m) của bạn:

@synthesize myDelegate;

Trong tệp tiêu đề FirstViewController của bạn, hãy đăng ký giao thức SecondDelegate bằng cách thực hiện điều này:

#import "SecondViewController.h"

@interface FirstViewController:UIViewController <SecondDelegate>

Bây giờ khi bạn khởi tạo SecondViewController trong FirstViewController, bạn nên làm như sau:

// If you're using a view controller built with Interface Builder.
SecondViewController *second = [[SecondViewController alloc] initWithNibName:"SecondViewController" bundle:[NSBundle mainBundle]];
// If you're using a view controller built programmatically.
SecondViewController *second = [SecondViewController new]; // Convenience initializer that uses alloc] init]
second.myString = @"This text is passed from firstViewController!";
second.myDelegate = self;
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];

Cuối cùng, trong tệp triển khai cho bộ điều khiển chế độ xem đầu tiên của bạn (FirstViewController.m), hãy triển khai phương thức của SecondDelegate cho secondViewControllerDismissed:

- (void)secondViewControllerDismissed:(NSString *)stringForFirst
{
    NSString *thisIsTheDesiredString = stringForFirst; //And there you have it.....
}

Bây giờ khi bạn sắp loại bỏ bộ điều khiển chế độ xem thứ hai, bạn muốn gọi phương thức được triển khai trong bộ điều khiển chế độ xem thứ nhất. Phần này đơn giản. Tất cả những gì bạn làm là, trong bộ điều khiển chế độ xem thứ hai, hãy thêm một số mã trước mã loại bỏ:

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!"];
}
[self dismissModalViewControllerAnimated:YES];

Các giao thức ủy nhiệm là CỰC KỲ, CỰC KỲ, CỰC KỲ hữu ích. Bạn sẽ rất tốt nếu làm quen với chúng :)

NSNotifications là một cách khác để thực hiện việc này, nhưng như một phương pháp hay nhất, tôi thích sử dụng nó hơn khi tôi muốn giao tiếp qua nhiều viewControllers hoặc đối tượng. Đây là câu trả lời tôi đã đăng trước đó nếu bạn tò mò về việc sử dụng NSNotifications: Kích hoạt các sự kiện trên nhiều bộ điều khiển chế độ xem từ một chuỗi trong appdelegate

BIÊN TẬP:

Nếu bạn muốn chuyển nhiều đối số, mã trước khi loại bỏ sẽ giống như sau:

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:argument2:argument3:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!" argument2:someObject argument3:anotherObject];
}
[self dismissModalViewControllerAnimated:YES];

Điều này có nghĩa là việc triển khai phương thức SecondDelegate bên trong firstViewController của bạn bây giờ sẽ giống như sau:

- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
    NSString thisIsTheDesiredString = stringForFirst;
    NSObject desiredObject1 = inObject1;
    //....and so on
}

Theo Hướng dẫn lập trình bộ điều khiển chế độ xem của Apple dành cho iOS ,ViewController thứ hai nên được loại bỏ trong bộ điều khiển chế độ xem trình bày, không phải trong bộ điều khiển đã trình bày.
Michael

Có vẻ như bạn chưa đặt đại biểu của UITableView. Bạn có thể đăng điều này như một câu hỏi, với mã bạn có và khoanh tròn lại không? Tôi có thể giúp bạn.
Sid

1
@Michael Tài liệu nói rằng việc gọi bỏ tự chuyển tiếp lệnh gọi đến bộ điều khiển chế độ xem trình bày. Ngoài ra, tự gọi điện cũng rõ ràng hơn vì theo cách đó bạn không cần phải lo lắng về việc chuyển đổi giữa PresentationViewController và parentViewController tùy thuộc vào phiên bản iOS mà bạn đang nhắm mục tiêu (5 hoặc trước đó).
Sid

1
@Resty Tôi đồng ý; các khối rất hữu ích. Tôi đã xem xét thay đổi câu trả lời này để hỗ trợ các khối tại một số thời điểm. Tuy nhiên trong trường hợp này, tôi để câu trả lời ủy quyền hiển thị ngay bây giờ vì nó cho chúng ta thêm một chút tự do để thao tác các đối tượng có thể được chuyển vào phương thức. Tôi chỉ lười biếng và sẽ cập nhật câu trả lời này để sử dụng khối sớm :)
Sid

1
@sid cảm ơn người anh em nó phù hợp với tôi nhưng bạn hơi cần phải sửa đổi. vì nhiều thứ đã thay đổi. vui lòng chỉnh sửa nó
ChenSmile

40

Tôi có thể hơi lạc lõng ở đây, nhưng tôi bắt đầu thích cú pháp khối hơn cách tiếp cận giao thức / đại biểu rất dài dòng. Nếu bạn tạo vc2 từ vc1, hãy có một thuộc tính trên vc2 mà bạn có thể đặt từ vc1 đó là một khối!

@property (nonatomic, copy) void (^somethingHappenedInVC2)(NSString *response);

Sau đó, khi có điều gì đó xảy ra trong vc2 mà bạn muốn nói với vc1, chỉ cần thực thi khối mà bạn đã xác định trong vc1!

self.somethingHappenedInVC2(@"Hello!");

Điều này cho phép bạn gửi dữ liệu từ vc2 trở lại vc1. Cũng giống như phép thuật. IMO, điều này dễ dàng hơn nhiều so với các giao thức. Các khối thật tuyệt vời và cần được đón nhận nhiều nhất có thể.

EDIT - Cải thiện ví dụ

Giả sử chúng ta có một mainVC mà chúng ta muốn tạm thời trình bày một modalVC bên trên để lấy một số thông tin đầu vào từ người dùng. Để trình bày modalVC đó từ mainVC, chúng ta cần cấp phát / init nó bên trong mainVC. Những thứ khá cơ bản. Khi chúng ta tạo đối tượng modalVC này, chúng ta cũng có thể thiết lập một thuộc tính khối trên nó cho phép chúng ta dễ dàng giao tiếp giữa cả hai đối tượng vc. Vì vậy, hãy lấy ví dụ ở trên và đặt thuộc tính follwing trong tệp .h của modalVC:

 @property (nonatomic, copy) void (^somethingHappenedInModalVC)(NSString *response);  

Sau đó, trong mainVC của chúng tôi, sau khi chúng tôi đã cấp phát / init'd một đối tượng modalVC mới, bạn đặt thuộc tính khối của modalVC như sau:

ModalVC *modalVC = [[ModalVC alloc] init];
modalVC.somethingHappenedInModalVC = ^(NSString *response) {
     NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
}

Vì vậy, chúng tôi chỉ đang thiết lập thuộc tính khối và xác định điều gì sẽ xảy ra khi khối đó được thực thi.

Cuối cùng, trong modalVC của chúng tôi, chúng tôi có thể có một tableViewController được hỗ trợ bởi một mảng chuỗi dataSource. Sau khi lựa chọn hàng được thực hiện, chúng ta có thể làm như sau:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      NSString *selectedString = self.dataSource[indexPath.row];
      self.somethingHappenedInModalVC(selectedString);
 }

Và tất nhiên, mỗi khi chúng ta chọn một hàng trong modalVC, chúng ta sẽ nhận được đầu ra bảng điều khiển từ dòng NSLog của chúng ta trong mainVC. Hy vọng rằng sẽ giúp!


1
Điều này có còn hoạt động khi sử dụng bảng phân cảnh không? Ngay bây giờ nó không làm việc cho tôi. Chỉ thoát với lỗi lldb. Sự khác biệt chính. Tôi có thể thấy là phân bổ của vc bây giờ là một luồng phân cảnh được khởi tạo. CHỈNH SỬA Và tôi đã trình bày trước khi tạo khối. Đã sửa.
malaki1974

2
Tôi đồng ý với bạn :) Tôi đã đăng câu trả lời của tôi khá lâu trước đây. Bây giờ, tôi chuyển đổi giữa việc sử dụng các khối / giao thức tùy thuộc vào cách sử dụng. Vì chủ đề này vẫn còn hoạt động cho đến ngày nay, nên tại một số thời điểm, tôi nên chỉnh sửa câu trả lời của mình để bao gồm các khối.
Sid

1
Câu trả lời này cần được chấp nhận vì điều này mang lại giải pháp trực quan nhất.
Sukitha Udugamasooriya

2
Trong số hai câu trả lời thích hợp, đây là câu trả lời tốt nhất!
kygcoleman

1
Đây là phương pháp ưa thích của tôi. Trong thời gian nhanh chóng, điều này sau đó được hoàn thành với việc đóng cửa. Tốt hơn nhiều sau đó ủy quyền và thông báo vì bạn không cần chỉ định các giao thức hoặc các hằng số thông báo "xấu xí" này. Nếu bạn đặt tên của biến trong vc được trình bày có đóng gói tốt, nó có thể là mã rất trực quan, ví dụ. Vc.didCancel, vc.didFinish ... Bạn có thể đặt những thứ này trong readyForSegue nếu vc trình bày nó (nếu bạn đang sử dụng segues).
HixField

4

hmm, hãy tìm trung tâm thông báo và gửi lại thông tin trong một thông báo. đây là táo đảm nhận nó - tôi thực hiện phương pháp này cá nhân trừ khi bất kỳ ai có bất kỳ đề xuất nào khác


Liên kết thực sự phức tạp quá mức, tất cả những gì bạn cần là một người quan sát (View Controller đầu tiên) và gửi thông báo từ thứ hai. Bạn có thể chỉ định bộ chọn cho một thông báo và cũng nhận được thông tin được gửi lại thông qua thông báo.
theiOSDude

2

Xác định giao thức đại biểu trong bộ điều khiển dạng xem thứ hai và đặt cái đầu tiên làm đại biểu của cái thứ hai.

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.