Câu hỏi Chuyển dữ liệu giữa các bộ điều khiển xem


Tôi mới vào iOS và Objective-C và toàn bộ mô hình MVC và tôi bị mắc kẹt với những điều sau đây:

Tôi có chế độ xem hoạt động như một biểu mẫu nhập dữ liệu và tôi muốn cung cấp cho người dùng tùy chọn để chọn nhiều sản phẩm. Các sản phẩm được liệt kê trên một chế độ xem khác với UITableViewController và tôi đã bật nhiều lựa chọn.

Câu hỏi của tôi là, làm cách nào để chuyển dữ liệu từ chế độ xem này sang chế độ xem khác? Tôi sẽ giữ các lựa chọn trên UITableView trong một mảng, nhưng làm thế nào để tôi chuyển nó trở lại dạng xem biểu mẫu nhập dữ liệu trước đó để nó có thể được lưu cùng với dữ liệu khác vào Dữ liệu cốt lõi khi gửi biểu mẫu?

Tôi đã lướt xung quanh và thấy một số người khai báo một mảng trong ứng dụng đại biểu. Tôi đã đọc một vài điều về Singletons nhưng không hiểu đó là gì và tôi đã đọc một vài điều về việc tạo ra một mô hình dữ liệu.

Điều gì sẽ là cách chính xác để thực hiện điều này và làm thế nào tôi sẽ đi về nó?


1206
2018-03-06 12:43


gốc


@Wain, có gì với thẻ meta? - Charles
@Charles, câu hỏi này là một ví dụ về những người có vấn đề với sự tương tác / mối quan hệ của lớp. Điều này xảy ra khá một chút và thường xuyên được trình bày sai và được gắn thẻ là ngôn ngữ / IDE. Đó là một nỗ lực để nhóm loại vấn đề này để hỗ trợ việc nhận dạng. Không đáng giá? - Wain
@Charles, điểm công bằng, nó thường liên quan đến sự hiểu biết của người dân về các trường hợp cũng như các mối quan hệ mà cũng nghiêng về phía một thẻ oop. - Wain
Đây là một câu hỏi đặt mọi người, từ thực tế sử dụng phương pháp này. Ví dụ tôi biết một vài người làm tất cả mọi thứ từ cùng một bộ điều khiển xem vì họ không thể sử dụng preparForSegue! nó bị lộn xộn - Xcoder


Các câu trả lời:


Câu hỏi này dường như rất phổ biến ở đây trên stackoverflow vì vậy tôi nghĩ rằng tôi sẽ cố gắng và đưa ra một câu trả lời tốt hơn để giúp đỡ những người bắt đầu trong thế giới của iOS như tôi.

Tôi hy vọng câu trả lời này là đủ rõ ràng để mọi người hiểu và tôi đã không bỏ lỡ bất cứ điều gì.

Chuyển tiếp dữ liệu

Truyền dữ liệu tới một bộ điều khiển xem từ một bộ điều khiển khung nhìn khác. Bạn sẽ sử dụng phương thức này nếu bạn muốn truyền một đối tượng / giá trị từ một bộ điều khiển khung nhìn đến một bộ điều khiển khung nhìn khác mà bạn có thể đang đẩy vào ngăn xếp điều hướng.

Trong ví dụ này, chúng ta sẽ có ViewControllerA và ViewControllerB

Để vượt qua BOOL giá trị từ ViewControllerA đến ViewControllerB chúng tôi sẽ làm như sau.

  1. trong ViewControllerB.h tạo thuộc tính cho BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. trong ViewControllerA bạn cần nói về ViewControllerB vì vậy hãy sử dụng

    #import "ViewControllerB.h"
    

    Sau đó, nơi bạn muốn tải xem ví dụ. didSelectRowAtIndex hoặc một số IBAction bạn cần đặt thuộc tính ViewControllerB trước khi bạn đẩy nó lên nav stack.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];
    

    Điều này sẽ thiết lập isSomethingEnabled trong ViewControllerB đến BOOL giá trị YES.

Chuyển tiếp Chuyển tiếp dữ liệu bằng cách sử dụng Phân đoạn

Nếu bạn đang sử dụng Bảng phân cảnh, bạn có nhiều khả năng sử dụng phân đoạn và sẽ cần quy trình này để chuyển dữ liệu về phía trước. Điều này tương tự như ở trên nhưng thay vì truyền dữ liệu trước khi bạn đẩy trình điều khiển chế độ xem, bạn sử dụng một phương thức có tên

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

Vì vậy, để vượt qua một BOOL từ ViewControllerA đến ViewControllerB chúng tôi sẽ làm như sau:

  1. trong ViewControllerB.h tạo thuộc tính cho BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. trong ViewControllerA bạn cần nói về ViewControllerB vì vậy hãy sử dụng

    #import "ViewControllerB.h"
    
  3. Tạo một khoảng cách từ ViewControllerA đến ViewControllerB trên bảng phân cảnh và cung cấp cho nó một số nhận dạng, trong ví dụ này, chúng tôi sẽ gọi nó "showDetailSegue"

  4. Tiếp theo, chúng ta cần thêm phương thức vào ViewControllerA được gọi khi bất kỳ khoảng cách nào được thực hiện, bởi vì điều này chúng ta cần phải phát hiện ra segue được gọi và sau đó làm điều gì đó. Trong ví dụ của chúng tôi, chúng tôi sẽ kiểm tra "showDetailSegue" và nếu điều đó được thực hiện, chúng tôi sẽ vượt qua BOOL có giá trị đối với ViewControllerB

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
            controller.isSomethingEnabled = YES;
        }
    }
    

    Nếu bạn đã nhúng các khung nhìn của mình trong một bộ điều khiển điều hướng, bạn cần thay đổi phương thức trên một chút để

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
            ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
            controller.isSomethingEnabled = YES;
        }
    }
    

    Điều này sẽ thiết lập isSomethingEnabled trong ViewControllerB đến BOOL giá trị YES.

Chuyển dữ liệu trở lại

Để chuyển dữ liệu trở lại từ ViewControllerB đến ViewControllerA bạn cần sử dụng Giao thức và đại biểu hoặc là Khối, sau này có thể được sử dụng như một cơ chế kết hợp lỏng lẻo cho các cuộc gọi lại.

Để làm điều này, chúng tôi sẽ làm ViewControllerA một đại biểu của ViewControllerB. Điều này cho phép ViewControllerB để gửi tin nhắn trở lại ViewControllerA cho phép chúng tôi gửi lại dữ liệu.

Dành cho ViewControllerA để được đại biểu ViewControllerB nó phải phù hợp với ViewControllerB's giao thức mà chúng ta phải xác định. Điều này cho biết ViewControllerA phương thức nào nó phải thực hiện.

  1. Trong ViewControllerB.h, bên dưới #import, nhưng ở trên @interface bạn chỉ định giao thức.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
    
  2. tiếp theo vẫn còn trong ViewControllerB.h bạn cần thiết lập delegate tài sản và tổng hợp trong ViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. Trong ViewControllerB chúng tôi gọi một tin nhắn trên delegate khi chúng tôi bật trình điều khiển chế độ xem.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. Đó là nó cho ViewControllerB. Bây giờ trong ViewControllerA.h, nói ViewControllerA nhập khẩu ViewControllerB và phù hợp với giao thức của nó.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. Trong ViewControllerA.m triển khai phương thức sau từ giao thức của chúng tôi

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
    
  6. Trước khi đẩy viewControllerB để chuyển hướng ngăn xếp, chúng ta cần phải nói ViewControllerB cái đó ViewControllerA là người được ủy quyền, nếu không chúng tôi sẽ gặp lỗi.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];
    

Tài liệu tham khảo


1557
2018-03-16 11:39



Chúng ta cũng phải đặt @class ViewControllerB; ở trên định nghĩa @protocol? Nếu không có nó tôi nhận được một lỗi "Loại dự kiến" trên ViewControllerB trong dòng: - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;  trong @protocol tờ khai - alan-p
Điều này làm việc tuyệt vời. Như alan-p nói, đừng quên viết @class ViewControllerB; phía trên giao thức nếu không bạn sẽ nhận được lỗi "Mong đợi loại". - Andrew Davis
bạn không cần các đại biểu để quay lại, chỉ cần sử dụng thư giãn. - malhal
Khi tôi đặt "viewControllerB.delegate = self;" trong ViewControllerB Tôi nhận được một lỗi. Gán cho 'id <ViewControllerBDelegate>' từ kiểu không tương thích 'ViewControllerB * const __strong', tôi không chắc mình đang làm gì sai. Có ai giúp được không? Thêm vào đó tôi đã phải thay đổi: initWithNib -> initWithNibName. - uplearnedu.com
nếu bạn đang sử dụng NavigationController bạn phải sử dụng [self.navigationController pushViewController:viewController animated:YES]; thay thế [self pushViewController:viewControllerB animated:YES]; - Nazir


Nhanh

Có tấn và tấn giải thích ở đây và xung quanh StackOverflow, nhưng nếu bạn là người mới bắt đầu chỉ cố gắng để có được một cái gì đó cơ bản để làm việc, hãy thử xem hướng dẫn YouTube này (Đó là những gì đã giúp tôi cuối cùng hiểu làm thế nào để làm điều đó).

Chuyển dữ liệu tới Trình điều khiển Chế độ xem tiếp theo

Sau đây là một ví dụ dựa trên video. Ý tưởng là để vượt qua một chuỗi từ trường văn bản trong Bộ điều khiển xem đầu tiên vào nhãn trong Bộ điều khiển chế độ xem thứ hai.

enter image description here

Tạo bố cục bảng phân cảnh trong Trình tạo giao diện. Để làm cho segue, bạn chỉ Điều khiển nhấp vào nút và kéo qua Trình điều khiển Chế độ xem Thứ hai.

Bộ điều khiển xem đầu tiên

Mã cho Bộ điều khiển Chế độ xem Đầu tiên là

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController

        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

Bộ điều khiển chế độ xem thứ hai

Và mã cho Bộ điều khiển Chế độ xem Thứ hai là

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

Đừng quên

  • Treo lên các cửa hàng cho UITextField và UILabel.
  • Đặt bộ điều khiển xem đầu tiên và thứ hai cho các tệp Swift thích hợp trong IB.

Chuyển dữ liệu trở lại Trình điều khiển Chế độ xem trước đó

Để chuyển dữ liệu từ bộ điều khiển xem thứ hai sang bộ điều khiển xem đầu tiên, bạn sử dụng một giao thức và một đại biểu. Video này là một bước đi rất rõ ràng mặc dù quá trình đó:

Sau đây là một ví dụ dựa trên video (với một vài sửa đổi).

enter image description here

Tạo bố cục bảng phân cảnh trong Trình tạo giao diện. Một lần nữa, để làm cho segue, bạn chỉ Điều khiển kéo từ nút đến Bộ điều khiển Chế độ xem Thứ hai. Đặt mã định danh segue thành showSecondViewController. Ngoài ra, đừng quên kết nối các cửa hàng và hành động bằng cách sử dụng các tên trong mã sau đây.

Bộ điều khiển xem đầu tiên

Mã cho Bộ điều khiển Chế độ xem Đầu tiên là

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

Lưu ý việc sử dụng tùy chỉnh của chúng tôi DataEnteredDelegate giao thức.

Bộ điều khiển và giao thức thứ hai

Mã cho trình điều khiển chế độ xem thứ hai là

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: class {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // making this a weak variable so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

Lưu ý rằng protocol nằm ngoài lớp View Controller.

Đó là nó. Chạy ứng dụng ngay bây giờ, bạn sẽ có thể gửi dữ liệu từ bộ điều khiển chế độ xem thứ hai đến đầu tiên.


132
2017-08-11 06:35



Với một số cập nhật Swift mới nhất, đây có phải là mô hình phổ biến để triển khai không? - piofusco
Hầu hết tất cả các cập nhật Swift mà tôi đã thấy là những thay đổi cú pháp tương đối nhỏ, không thay đổi cách dữ liệu được truyền giữa các bộ điều khiển khung nhìn. Nếu tôi tìm hiểu về bất kỳ thay đổi lớn như thế, tôi sẽ cập nhật câu trả lời của mình. - Suragch
Offtopic - iOS có một cách xấu xí để truyền các tham số đến các bộ điều khiển xem mới, không thể tin được - bạn phải thiết lập các tham số không ở một nơi khi bạn thực hiện cuộc gọi, nhưng trong một số khác. Android có cách tiếp cận tốt hơn trong lĩnh vực này - khi bạn bắt đầu Hoạt động, bạn có thể chuyển bất kỳ dữ liệu nào (tốt, gần như) thông qua Mục đích bắt đầu của nó. Dễ dàng. Không cần phải đúc hay gì đó. Việc trả lại giá trị trả lại cho người gọi cũng là một điều cần thiết, không cần ủy nhiệm. Tất nhiên nó có thể sử dụng phương pháp tiếp cận xấu xí quá, không có vấn đề đó)) - Mixaz
@Himanshu, đầu tiên có được một tham chiếu đến bộ điều khiển xem thứ hai. Sau đó cập nhật biến công khai mà nó chứa. - Suragch
@Mật ong. Tôi nghĩ từ "đại biểu" là khó hiểu. Hãy để tôi sử dụng từ "công nhân". "Công nhân" (bộ điều khiển xem đầu tiên) làm bất cứ điều gì "ông chủ" (bộ điều khiển xem thứ hai) nói với nó để làm. Các "ông chủ" không biết ai là "công nhân" của nó sẽ được; nó có thể là bất cứ ai. Vì vậy, trong bộ điều khiển xem đầu tiên ("công nhân" lớp), nó nói, tôi sẽ là "công nhân" của bạn. Bạn cho tôi biết những gì để viết trong nhãn và tôi sẽ làm điều đó cho bạn. Như vậy, secondViewController.delegate = self có nghĩa là "Tôi đồng ý làm công việc của sếp." Xem câu trả lời này cho một ví dụ khác và giải thích thêm. - Suragch


M trong MVC là cho "Model" và trong mô hình MVC, vai trò của các lớp mô hình là quản lý dữ liệu của một chương trình. Một mô hình ngược lại với một khung nhìn - một cái nhìn biết cách hiển thị dữ liệu, nhưng nó không biết gì về dữ liệu, trong khi mô hình biết mọi thứ về cách làm việc với dữ liệu, nhưng không có gì về cách hiển thị nó. Các mô hình có thể phức tạp, nhưng chúng không nhất thiết phải là - mô hình cho ứng dụng của bạn có thể đơn giản như một chuỗi các chuỗi hoặc từ điển.

Vai trò của bộ điều khiển là dàn xếp giữa chế độ xem và mô hình. Vì vậy, họ cần một tham chiếu đến một hoặc nhiều đối tượng xem và một hoặc nhiều đối tượng mô hình. Giả sử mô hình của bạn là một mảng từ điển, với mỗi từ điển đại diện cho một hàng trong bảng của bạn. Chế độ xem gốc cho ứng dụng của bạn hiển thị bảng đó và có thể chịu trách nhiệm tải mảng từ một tệp. Khi người dùng quyết định thêm một hàng mới vào bảng, họ nhấn vào một số nút và bộ điều khiển của bạn tạo một từ điển mới (có thể thay đổi) và thêm nó vào mảng. Để điền vào hàng, bộ điều khiển tạo bộ điều khiển xem chi tiết và cung cấp cho nó từ điển mới. Bộ điều khiển xem chi tiết điền vào từ điển và trả về. Từ điển đã là một phần của mô hình, vì vậy không có gì khác cần phải xảy ra.


118
2018-03-06 13:49





Có nhiều cách khác nhau để có thể nhận dữ liệu đến một lớp khác trong iOS. Ví dụ -

  1. Khởi tạo trực tiếp sau khi phân bổ một lớp khác.
  2. Ủy quyền - để chuyển dữ liệu trở lại
  3. Thông báo - để truyền dữ liệu đến nhiều lớp cùng một lúc
  4. Tiết kiệm trong NSUserDefaults - để truy cập sau
  5. Lớp Singleton
  6. Cơ sở dữ liệu và các cơ chế lưu trữ khác như plist, v.v.

Nhưng đối với kịch bản đơn giản của việc chuyển một giá trị cho một lớp khác có phân bổ được thực hiện trong lớp hiện tại, phương thức phổ biến nhất và ưa thích sẽ là thiết lập trực tiếp của các giá trị sau khi cấp phát. Điều này được thực hiện như sau:-

Chúng ta có thể hiểu nó bằng cách sử dụng hai bộ điều khiển - Controller1 và Controller2 

Giả sử trong lớp Controller1 bạn muốn tạo đối tượng Controller2 và đẩy nó với một giá trị String đang được truyền. Điều này có thể được thực hiện như sau:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

Trong việc thực hiện lớp Controller2 sẽ có chức năng này như

@interface Controller2  : NSObject

@property (nonatomic , strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; //or self.stringPassed = value
}

@end

Bạn cũng có thể trực tiếp thiết lập các thuộc tính của lớp Controller2 theo cách tương tự như sau:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];  
    [self pushViewController:obj animated:YES];
}

Để chuyển nhiều giá trị, bạn có thể sử dụng nhiều tham số như: -

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date]; 

Hoặc nếu bạn cần truyền hơn 3 tham số liên quan đến một tính năng chung, bạn có thể lưu trữ các giá trị cho một lớp Model và chuyển modelObject đó tới lớp tiếp theo

ModelClass *modelObject = [[ModelClass alloc] init]; 
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

Vì vậy, trong ngắn hạn nếu bạn muốn -

1) set the private variables of the second class initialise the values by calling a custom function and passing the values.
2) setProperties do it by directlyInitialising it using the setter method.
3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process.

Hi vọng điêu nay co ich


85
2018-04-08 10:24





Sau nhiều nghiên cứu hơn, dường như các giao thức và đại biểu là đúng / Apple thích cách làm điều này.

Tôi đã sử dụng ví dụ này

Chia sẻ dữ liệu giữa các bộ điều khiển xem và các đối tượng khác @ iPhone Dev SDK

Làm việc tốt và cho phép tôi vượt qua một chuỗi và một mảng về phía trước và trở lại giữa các quan điểm của tôi.

Cảm ơn tất cả sự giúp đỡ của bạn


74
2018-03-13 21:20



không sử dụng giao thức và đại biểu, chỉ cần sử dụng thư giãn. - malhal
@malhal Nếu bạn không sử dụng bảng phân cảnh thì sao ?? - Evan R
Tôi cũng ghét các giao thức và đại biểu vô dụng. @malhal - Dawn Song
@EvanR Bạn có thể tạo và thực hiện phân đoạn trong mã. Tất cả đều giống nhau. - Dawn Song
Về cơ bản, toàn bộ QA trên trang này là "từ những ngày trước khi xem vùng chứa". Bạn sẽ không bao giờ trong một triệu năm bận tâm với các giao thức hoặc đại biểu bây giờ. Tuy nhiên, mọi thứ bạn làm trên màn hình bất kỳ là chế độ xem vùng chứa, vì vậy, câu hỏi thực sự không còn tồn tại nữa - bạn đã có tất cả các tham chiếu "lên và xuống" từ tất cả các chế độ xem vùng chứa. - Fattie


Tôi tìm thấy phiên bản đơn giản và thanh lịch nhất với các khối đi qua. Hãy đặt tên cho bộ điều khiển xem đợi dữ liệu trả về là "A" và trả về bộ điều khiển xem là "B". Trong ví dụ này, chúng tôi muốn nhận được 2 giá trị: đầu tiên của Type1 và thứ hai của Type2.

Giả sử chúng ta sử dụng Storyboard, bộ điều khiển đầu tiên đặt khối gọi lại, ví dụ trong quá trình chuẩn bị segue:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

và bộ điều khiển xem "B" nên khai báo thuộc tính gọi lại, BViewController.h:

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

Hơn trong tập tin thực hiện BViewController.m sau khi chúng tôi có giá trị mong muốn để trả về gọi lại của chúng tôi nên được gọi là:

if (self.callback)
    self.callback(value1, value2);

Một điều cần nhớ là việc sử dụng khối thường cần quản lý các tham chiếu mạnh mẽ và __weak như được giải thích đây


59
2017-10-14 18:11



Tại sao giá trị không là tham số cho khối gọi lại chứ không phải là thuộc tính riêng biệt? - Timuçin
đây là giải pháp tốt nhất. Nếu mô hình là một. Nếu bạn cần cập nhật nhiều hơn một bộ điều khiển xem thì hãy làm theo Thông báo hoặc đại biểu. - Ankish Jain


Có một số thông tin tốt trong nhiều câu trả lời được đưa ra, nhưng không ai trả lời đầy đủ câu hỏi.

Câu hỏi đặt ra về việc truyền thông tin giữa các bộ điều khiển xem. Ví dụ cụ thể được đưa ra yêu cầu về việc truyền thông tin giữa các chế độ xem, nhưng do tính mới được tự khai cho iOS, áp phích gốc có thể có nghĩa giữa các viewControllers, chứ không phải giữa các chế độ xem (mà không có bất kỳ sự tham gia nào từ các ViewControllers). Dường như tất cả các câu trả lời tập trung vào hai bộ điều khiển xem, nhưng nếu ứng dụng phát triển cần phải liên quan đến nhiều hơn hai bộ điều khiển xem trong trao đổi thông tin thì sao?

Các poster ban đầu cũng được hỏi về Singletons và việc sử dụng AppDelegate. Những câu hỏi này cần phải được trả lời.

Để giúp bất cứ ai khác nhìn vào câu hỏi này, ai muốn câu trả lời đầy đủ, tôi sẽ cố gắng cung cấp nó.

Các kịch bản ứng dụng

Thay vì có một cuộc thảo luận trừu tượng, có tính giả thuyết cao, nó sẽ giúp bạn có những ứng dụng cụ thể. Để giúp xác định tình huống hai bộ điều khiển và tình huống điều khiển nhiều hơn hai lần, tôi sẽ định nghĩa hai kịch bản ứng dụng cụ thể.

Kịch bản một: tối đa hai bộ điều khiển xem bao giờ cần phải chia sẻ thông tin. Xem sơ đồ một.

diagram of original problem

Có hai bộ điều khiển xem trong ứng dụng. Có một ViewControllerA (Data Entry Form) và View Controller B (Danh sách sản phẩm). Các mục được chọn trong danh sách sản phẩm phải khớp với các mục được hiển thị trong hộp văn bản trong biểu mẫu nhập dữ liệu. Trong trường hợp này, ViewControllerA và ViewControllerB phải giao tiếp trực tiếp với nhau và không có bộ điều khiển xem nào khác.

Kịch bản hai: nhiều hơn hai bộ điều khiển xem cần chia sẻ cùng một thông tin. Xem sơ đồ hai.

home inventory application diagram

Có bốn bộ điều khiển xem trong ứng dụng. Nó là một ứng dụng dựa trên tab để quản lý hàng tồn kho nhà. Ba trình điều khiển chế độ xem hiển thị các chế độ xem được lọc khác nhau của cùng một dữ liệu:

  • ViewControllerA - Mặt hàng xa xỉ
  • ViewControllerB - Các mục không được bảo hiểm
  • ViewControllerC - Toàn bộ Khoảng không quảng cáo Trang chủ
  • ViewControllerD - Thêm biểu mẫu mục mới

Bất cứ khi nào một mục riêng lẻ được tạo hoặc chỉnh sửa, nó cũng phải đồng bộ hóa với các bộ điều khiển khung nhìn khác. Ví dụ, nếu chúng ta thêm một thuyền trong ViewControllerD, nhưng nó chưa được bảo hiểm, thì thuyền phải xuất hiện khi người dùng vào ViewControllerA (Luxury Items), và cũng ViewControllerC (Entire Home Inventory), nhưng không phải khi người dùng truy cập ViewControllerB (Các mục không được bảo hiểm). Chúng ta cần quan tâm đến việc không chỉ thêm các mục mới, mà còn xóa các mục (có thể được cho phép từ bất kỳ bộ điều khiển nào trong bốn bộ điều khiển xem) hoặc chỉnh sửa các mục hiện có (có thể được cho phép từ "Thêm mục mới", sửa lại để chỉnh sửa).

Vì tất cả các bộ điều khiển xem cần chia sẻ cùng một dữ liệu, tất cả bốn bộ điều khiển xem cần phải được đồng bộ hóa và do đó cần phải có một số loại giao tiếp cho tất cả các bộ điều khiển chế độ xem khác, bất cứ khi nào bất kỳ bộ điều khiển chế độ xem nào thay đổi dữ liệu bên dưới. Rõ ràng là chúng tôi không muốn mỗi bộ điều khiển xem giao tiếp trực tiếp với mỗi bộ điều khiển xem khác trong trường hợp này. Trong trường hợp nó không rõ ràng, hãy xem xét nếu chúng ta có 20 bộ điều khiển xem khác nhau (thay vì chỉ 4). Làm thế nào khó khăn và dễ bị lỗi nó sẽ được thông báo cho mỗi người trong số 19 bộ điều khiển xem khác bất cứ lúc nào một bộ điều khiển xem thực hiện một sự thay đổi?

Giải pháp: Đại biểu và Mẫu quan sát viên, và Singletons

Trong kịch bản một, chúng tôi có một số giải pháp khả thi, như các câu trả lời khác đã đưa ra

  • phân đoạn
  • đại biểu
  • đặt thuộc tính trực tiếp trên bộ điều khiển chế độ xem
  • NSUserDefaults (thực sự là một sự lựa chọn tồi)

Trong kịch bản hai, chúng tôi có các giải pháp khả thi khác:

  • Mẫu quan sát
  • Singletons

A singleton là một thể hiện của một lớp, ví dụ đó là cá thể duy nhất tồn tại trong suốt cuộc đời của nó. Một singleton được tên của nó từ thực tế rằng nó là trường hợp duy nhất. Thông thường các nhà phát triển sử dụng các trình đơn có các phương thức lớp đặc biệt để truy cập chúng.

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed once in the
    // lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

Bây giờ chúng ta hiểu những gì một singleton là, chúng ta hãy thảo luận làm thế nào một singleton phù hợp với mô hình quan sát. Dạng quan sát được sử dụng cho một đối tượng để phản hồi các thay đổi của một đối tượng khác. Trong kịch bản thứ hai, chúng tôi có bốn bộ điều khiển chế độ xem khác nhau, tất cả những người muốn biết về các thay đổi đối với dữ liệu cơ bản. "Dữ liệu cơ bản" nên thuộc về một cá thể đơn lẻ, một singleton. Các "biết về những thay đổi" được thực hiện bằng cách quan sát những thay đổi được thực hiện cho singleton.

Ứng dụng kiểm kê nhà sẽ có một cá thể của một lớp được thiết kế để quản lý danh sách các mục khoảng không quảng cáo. Người quản lý sẽ quản lý một bộ sưu tập các mặt hàng gia dụng. Sau đây là định nghĩa lớp cho trình quản lý dữ liệu:

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

Khi bộ sưu tập các mục kiểm kê nhà thay đổi, các bộ điều khiển khung nhìn cần phải được nhận biết về thay đổi này. Định nghĩa lớp ở trên không làm cho nó rõ ràng như thế nào điều này sẽ xảy ra. Chúng ta cần tuân theo mẫu người quan sát. Các bộ điều khiển xem phải chính thức quan sát sharedManager. Có hai cách để quan sát một đối tượng khác:

  • Quan sát giá trị-khóa (KVO)
  • NSNotificationCenter.

Trong kịch bản hai, chúng tôi không có một tài sản duy nhất của HouseholdInventoryManager mà có thể được quan sát bằng cách sử dụng KVO. Bởi vì chúng tôi không có một tài sản duy nhất mà có thể dễ dàng quan sát được, nên mô hình quan sát viên, trong trường hợp này, phải được thực hiện bằng cách sử dụng NSNotificationCenter. Mỗi bộ điều khiển xem bốn sẽ đăng ký thông báo và sharedManager sẽ gửi thông báo đến trung tâm thông báo khi thích hợp. Trình quản lý khoảng không cần phải biết bất kỳ điều gì về bộ điều khiển chế độ xem hoặc các phiên bản của bất kỳ lớp nào khác có thể muốn biết khi nào bộ sưu tập các mục khoảng không quảng cáo thay đổi; NSNotificationCenter quản lý các chi tiết triển khai này. Bộ điều khiển chế độ xem chỉ cần đăng ký thông báo và trình quản lý dữ liệu chỉ cần đăng thông báo.

Nhiều lập trình viên mới bắt đầu tận dụng thực tế là luôn có chính xác Ủy nhiệm ứng dụng trong thời gian tồn tại của ứng dụng, có thể truy cập toàn cầu. Bắt đầu lập trình viên sử dụng thực tế này để các đối tượng và chức năng công cụ vào appDelegate như là một sự tiện lợi cho việc truy cập từ bất cứ nơi nào khác trong ứng dụng. Chỉ vì AppDelegate là một singleton không có nghĩa là nó nên thay thế tất cả các singletons khác. Đây là một thực hành kém vì nó đặt quá nhiều gánh nặng lên một lớp, phá vỡ các thực hành hướng đối tượng tốt. Mỗi lớp nên có một vai trò rõ ràng dễ hiểu, thường chỉ bằng tên của lớp.

Bất cứ khi nào Đại biểu ứng dụng của bạn bắt đầu trở nên cồng kềnh, hãy bắt đầu xóa bỏ chức năng thành người độc thân. Ví dụ, ngăn xếp dữ liệu cốt lõi không được để trong AppDelegate, nhưng thay vào đó nên được đặt trong lớp riêng của nó, một lớp coreDataManager.

Tài liệu tham khảo


46
2018-04-05 22:04





Có nhiều phương pháp để chia sẻ dữ liệu.

  1. Bạn luôn có thể chia sẻ dữ liệu bằng NSUserDefaults. Đặt giá trị bạn muốn chia sẻ liên quan đến khóa bạn chọn và nhận giá trị từ NSUserDefault liên quan đến khóa đó trong trình điều khiển chế độ xem tiếp theo.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. Bạn chỉ có thể tạo thuộc tính trong viewcontrollerA. Tạo một đối tượng viewcontrollerA trong viewcontrollerB và gán giá trị mong muốn cho thuộc tính đó.

  3. Bạn cũng có thể tạo đại biểu tùy chỉnh cho việc này.


37
2017-09-27 10:38



Mục đích điển hình của NSUserDefaults là lưu trữ các sở thích người dùng tồn tại giữa các lần thực thi ứng dụng, vì vậy mọi thứ được lưu trữ ở đây sẽ vẫn ở đây trừ khi được loại bỏ một cách rõ ràng. Đó là một ý tưởng thực sự tồi để sử dụng điều này để truyền thông tin giữa các bộ điều khiển xem (hoặc bất kỳ đối tượng nào khác) trong một ứng dụng. - José González