Cách vượt qua chuẩn bịForSegue: một đối tượng


358

Tôi có nhiều chú thích trong mapview (có rightCalloutAccessorynút). Nút sẽ thực hiện một segue từ này mapviewđến a tableview. Tôi muốn chuyển tableviewmột đối tượng khác (chứa dữ liệu) tùy thuộc vào nút chú thích nào được nhấp.

Ví dụ: (hoàn toàn tạo nên)

  • chú thích1 (Austin) -> truyền dữ liệu obj 1 (có liên quan đến Austin)
  • annotation2 (Dallas) -> truyền dữ liệu obj 2 (có liên quan đến Dallas)
  • annotation3 (Houston) -> truyền dữ liệu obj 3 và cứ thế ... (bạn hiểu ý)

Tôi có thể phát hiện nút chú thích nào đã được nhấp.

Tôi đang sử dụng prepareForSegue: để truyền dữ liệu obj đến đích ViewController. Vì tôi không thể thực hiện cuộc gọi này có thêm một đối số cho obj dữ liệu tôi yêu cầu, một số cách thanh lịch để đạt được hiệu ứng tương tự (dữ liệu động obj) là gì?

Bất kỳ lời khuyên sẽ được đánh giá cao.


Câu trả lời:


674

Chỉ cần lấy một tham chiếu đến trình điều khiển khung nhìn đích trong prepareForSegue:phương thức và chuyển bất kỳ đối tượng nào bạn cần đến đó. Đây là một ví dụ ...

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Make sure your segue name in storyboard is the same as this line
    if ([[segue identifier] isEqualToString:@"YOUR_SEGUE_NAME_HERE"])
    {
        // Get reference to the destination view controller
        YourViewController *vc = [segue destinationViewController];

        // Pass any objects to the view controller here, like...
        [vc setMyObjectHere:object];
    }
}

CÁCH MẠNG: Bạn cũng có thể sử dụng performSegueWithIdentifier:sender:phương pháp để kích hoạt chuyển đổi sang chế độ xem mới dựa trên lựa chọn hoặc nhấn nút.

Ví dụ, xem xét tôi có hai bộ điều khiển xem. Nút đầu tiên chứa ba nút và nút thứ hai cần biết nút nào đã được nhấn trước khi chuyển đổi. Bạn có thể nối các nút IBActiontheo mã trong đó sử dụng performSegueWithIdentifier:phương thức, như thế này ...

// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
    [self performSegueWithIdentifier:@"MySegue" sender:sender];
}

// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"MySegue"]) {

        // Get destination view
        SecondView *vc = [segue destinationViewController];

        // Get button tag number (or do whatever you need to do here, based on your object
        NSInteger tagIndex = [(UIButton *)sender tag];

        // Pass the information to your destination view
        [vc setSelectedButton:tagIndex];
    }
}

EDIT: Ứng dụng demo mà tôi đã đính kèm ban đầu đã được sáu năm tuổi, vì vậy tôi đã gỡ bỏ nó để tránh mọi sự nhầm lẫn.


Cảm ơn bạn nhưng tôi muốn thiết lập [vc setMyObjectHere:object];điều này một cách năng động. tức là obj1 cho button1, obj2 cho button2 Vấn đề là tôi không thể vượt qua một đối số. Có cách nào khác không?
chizzle

2
Tôi đã cập nhật bài viết của mình với một ví dụ có thể tải xuống về những gì tôi đang nói.
Simon

đã làm việc Cảm ơn nhiều. Như một lưu ý phụ, hãy chuẩn bịForSegue: có một đối số UIControl là lớp cha của UIButton (do đó có thể nhận được thẻ): D
chizzle

Chuẩn bịForSegue có được gọi ngay cả khi bạn chỉ cần nhấn vào bộ điều khiển điều hướng mà không có bảng phân cảnh không?
zakdances

Đây là một trong những câu trả lời thấu đáo nhất mà tôi từng thấy. Các mẫu mã tốt, giải quyết vấn đề, cung cấp mẫu có thể tải xuống ... wow. Tôi rất ấn tượng!
lewiguez

82

Đôi khi nó rất hữu ích để tránh tạo ra sự phụ thuộc thời gian biên dịch giữa hai bộ điều khiển khung nhìn. Đây là cách bạn có thể làm điều đó mà không cần quan tâm đến loại trình điều khiển xem đích:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController respondsToSelector:@selector(setMyData:)]) {
        [segue.destinationViewController performSelector:@selector(setMyData:) 
                                              withObject:myData];
    } 
}

Vì vậy, miễn là bộ điều khiển xem đích của bạn khai báo một thuộc tính công cộng, ví dụ:

@property (nonatomic, strong) MyData *myData;

bạn có thể đặt thuộc tính này trong trình điều khiển xem trước như tôi đã mô tả ở trên.


12
Đây thực sự là một vấn đề quan điểm. Tôi chưa (chưa) có một tình huống mà tôi không muốn kiểm soát các bộ điều khiển xem một cách chặt chẽ, mặc dù tôi đánh giá cao những gì bạn đang nói và nó có thể quan trọng trong trường hợp phù hợp.
Simon

2
@Simon: yeah, bạn chỉ cần chọn cách tiếp cận tốt nhất cho bạn. Ví dụ, trong ứng dụng tôi đang làm việc, cách tiếp cận của tôi rất có ý nghĩa vì tôi tiếp tục thêm các bộ điều khiển xem cần cùng một đối tượng dữ liệu. Có thể nối chúng với chỉ một segue và biết rằng họ sẽ nhận được dữ liệu phù hợp là siêu tiện lợi.
Macondo2Seatussy

10
Đó không phải là vấn đề về quan điểm, nó chỉ sai :) Cụm từ "Câu trả lời được chấp nhận không phải là cách tốt nhất để làm điều này" là sai. Nó nên đọc "Trong một số trường hợp nhất định, bạn cần phải làm điều này ..."
Fattie

2
Tôi thích phương pháp của Simon. Vì nó sẽ cho tôi biết các lỗi tại thời gian biên dịch. Ví dụ: nếu tôi bỏ lỡ khai báo myData trong trình điều khiển khung nhìn đích, tôi sẽ biết ngay lập tức. Nhưng đối với kịch bản của bạn, cách tiếp cận của bạn có vẻ tốt!
Abdurrahman Mubeen Ali

14
Cách tiếp cận này hoàn toàn tạo ra sự phụ thuộc, vì bạn yêu cầu bộ điều khiển khung nhìn đích phải có một setMyData:. Đó là một sự phụ thuộc. Việc bạn sử dụng bộ chọn để tránh lỗi biên dịch nên được coi là điểm yếu trong cách tiếp cận của bạn, không phải là lợi ích. Điều gây sốc cho tôi là có bao nhiêu nhà phát triển đã mất khái niệm rằng biên dịch lỗi thời gian nên được ưu tiên hơn so với lỗi thời gian chạy.
Nate

21

Trong Swift 4.2 tôi sẽ làm một cái gì đó như thế:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let yourVC = segue.destination as? YourViewController {
        yourVC.yourData = self.someData
    }
}

Bạn có cần gọi: `super.prepare (for: segue, sender: sender)`?
Andrew K

16

Tôi có một lớp người gửi , như thế này

@class MyEntry;

@interface MySenderEntry : NSObject
@property (strong, nonatomic) MyEntry *entry;
@end

@implementation MySenderEntry
@end

Tôi sử dụng lớp người gửi này để truyền các đối tượng đếnprepareForSeque:sender:

-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
    MySenderEntry *sender = [MySenderEntry new];
    sender.entry = [_entries objectAtIndex:indexPath.row];
    [self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}

-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
        NSAssert([sender isKindOfClass:[MySenderEntry class]], @"MySenderEntry");
        MySenderEntry *senderEntry = (MySenderEntry*)sender;
        MyEntry *entry = senderEntry.entry;
        NSParameterAssert(entry);

        [segue destinationViewController].delegate = self;
        [segue destinationViewController].entry = entry;
        return;
    }

    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
        // ...
        return;
    }

    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
        // ...
        return;
    }
}

chúng ta đang sử dụng prepareForSegue hay chúng ta đang thực hiện nó và giả định là chuẩn bịForSegue được gọi là chính nó, tức là chúng ta không cần phải làm gì đó như[self prepareForSegue]
Mật ong

15

Tôi đã gặp câu hỏi này khi tôi đang cố gắng học cách truyền dữ liệu từ Trình điều khiển xem này sang Trình điều khiển khác. Tôi cần một cái gì đó trực quan để giúp tôi học hỏi, vì vậy câu trả lời này là một bổ sung cho những người khác đã ở đây. Nó là một chút tổng quát hơn so với câu hỏi ban đầu nhưng nó có thể được điều chỉnh để làm việc.

Ví dụ cơ bản này hoạt động như thế này:

nhập mô tả hình ảnh ở đây

Ý tưởng là chuyển một chuỗi từ trường văn bản trong Trình điều khiển Chế độ xem Đầu tiên sang nhãn trong Trình điều khiển Chế độ xem Thứ hai.

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

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

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

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

}

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

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
    }

}

Nhớ

  • Tạo sự khác biệt bằng controlcách nhấp vào nút và kéo nó sang Bộ điều khiển Chế độ xem thứ hai.
  • Móc ra các cửa hàng cho UITextFieldUILabel.
  • Đặt Bộ điều khiển Chế độ xem thứ nhất và thứ hai thành các tệp Swift thích hợp trong IB.

Nguồn

Cách gửi dữ liệu qua segue (swift) (hướng dẫn trên YouTube)

Xem thêm

Xem bộ điều khiển: Truyền dữ liệu về phía trước và chuyển dữ liệu trở lại (câu trả lời đầy đủ hơn)


5

Đối với Swift, hãy sử dụng

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var segueID = segue.identifier

    if(segueID! == "yourSegueName"){

        var yourVC:YourViewController = segue.destinationViewController as YourViewController

        yourVC.objectOnYourVC = setObjectValueHere!

    }
}

4

Tôi đã triển khai một thư viện với một danh mục trên UIViewControll để đơn giản hóa thao tác này. Về cơ bản, bạn đặt các tham số bạn muốn chuyển qua trong một NSDadata được liên kết với mục UI đang thực hiện segue. Nó hoạt động với segues thủ công quá.

Ví dụ, bạn có thể làm

[self performSegueWithIdentifier:@"yourIdentifier" parameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

cho một segue thủ công hoặc tạo một nút với một segue và sử dụng

[button setSegueParameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

Nếu bộ điều khiển khung nhìn đích không tương thích mã hóa giá trị khóa cho một khóa, không có gì xảy ra. Nó cũng hoạt động với các giá trị khóa (hữu ích cho các khoảng cách thư giãn). Kiểm tra nó ở đây https://github.com/stefanomondino/SMQuickSegue


2

Giải pháp của tôi cũng tương tự.

// In destination class: 
var AddressString:String = String()

// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   if (segue.identifier == "seguetobiddetailpagefromleadbidder")
    {
        let secondViewController = segue.destinationViewController as! BidDetailPage
        secondViewController.AddressString = pr.address as String
    }
}

1

Chỉ cần sử dụng chức năng này.

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let index = CategorytableView.indexPathForSelectedRow
    let indexNumber = index?.row
    let VC = segue.destination as! DestinationViewController
   VC.value = self.data

}

0

Tôi đã sử dụng giải pháp này để tôi có thể giữ lời gọi của segue và giao tiếp dữ liệu trong cùng một chức năng:

private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?

func performSegue(withIdentifier identifier: String, sender: Any?, completion: @escaping (UIStoryboardSegue, Any?) -> Void) {
    self.segueCompletion = completion;
    self.performSegue(withIdentifier: identifier, sender: sender);
    self.segueCompletion = nil
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    self.segueCompletion?(segue, sender)
}

Một trường hợp sử dụng sẽ là một cái gì đó như:

func showData(id : Int){
    someService.loadSomeData(id: id) {
        data in
        self.performSegue(withIdentifier: "showData", sender: self) {
            storyboard, sender in
            let dataView = storyboard.destination as! DataView
            dataView.data = data
        }
    }
}

Điều này dường như hoạt động với tôi, tuy nhiên, tôi không chắc chắn 100% rằng các chức năng thực hiện và chuẩn bị luôn được thực hiện trên cùng một luồng.

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.