在视图控制器之间传递数据

在视图控制器之间传递数据

在iOS开发中,视图控制器之间的数据传递是一个常见需求。本文将详细介绍多种在视图控制器间传递数据的方法,包括正向传递、反向传递等。

正向传递数据

直接设置属性

若要将数据从一个视图控制器传递到另一个视图控制器,可直接设置目标视图控制器的属性。以下是示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
// ViewControllerB.h
@interface ViewControllerB : UIViewController
@property (nonatomic, assign) BOOL isSomethingEnabled;
@end

// ViewControllerA.m
#import "ViewControllerB.h"
- (void)loadViewControllerB {
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self.navigationController pushViewController:viewControllerB animated:YES];
}

使用Segue传递

若使用Storyboards,可利用Segue正向传递数据。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// ViewControllerB.h
@interface ViewControllerB : UIViewController
@property (nonatomic, assign) BOOL isSomethingEnabled;
@end

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

反向传递数据

使用协议和代理

为实现从ViewControllerBViewControllerA反向传递数据,可使用协议和代理。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// ViewControllerB.h
@class ViewControllerB;
@protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
@end

@interface ViewControllerB : UIViewController
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
@end

// ViewControllerB.m
- (void)popViewController {
NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
[self.navigationController popViewControllerAnimated:YES];
}

// ViewControllerA.h
#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
@end

// ViewControllerA.m
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item {
NSLog(@"This was returned from ViewControllerB %@", item);
}

// 在ViewControllerA中推送ViewControllerB时设置代理
- (void)pushViewControllerB {
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self;
[[self navigationController] pushViewController:viewControllerB animated:YES];
}

使用Block

Block是匿名函数,也可用于反向传递数据。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// ControllerA.h
@property void(^selectedVoucherBlock)(NSString *);

// ControllerA.m
- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained typeof(self) weakSelf = self;
self.selectedVoucherBlock = ^(NSString *voucher) {
weakSelf->someLabel.text = voucher;
};
}

// 推送ControllerB
- (void)pushToControllerB {
ControllerB *vc = [[ControllerB alloc] initWithNib:@"ControllerB" bundle:nil];
vc.sourceVC = self;
[self.navigationController pushViewController:vc animated:NO];
}

// ControllerB.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *voucher = vouchersArray[indexPath.row];
if (sourceVC.selectVoucherBlock) {
sourceVC.selectVoucherBlock(voucher);
}
[self.navigationController popToViewController:sourceVC animated:YES];
}

使用NSNotificationCenter传递数据

可使用NSNotificationCenter在不同类之间传递数据。示例如下:

1
2
3
4
5
6
7
8
9
10
11
// 注册观察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

// 处理通知
- (void)handleDeepLinking:(NSNotification *) notification {
id someObject = notification.object;
}

// 发布通知
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

Swift实现示例

正向传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// FirstViewController.swift
import UIKit

class FirstViewController: UIViewController {
@IBOutlet weak var textField: UITextField!

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

// SecondViewController.swift
import UIKit

class SecondViewController: UIViewController {
@IBOutlet weak var label: UILabel!
var receivedString = ""

override func viewDidLoad() {
super.viewDidLoad()
label.text = receivedString
}
}

反向传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// FirstViewController.swift
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
}
}

// SecondViewController.swift
import UIKit

protocol DataEnteredDelegate: AnyObject {
func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {
weak var delegate: DataEnteredDelegate? = nil
@IBOutlet weak var textField: UITextField!

@IBAction func sendTextBackButton(sender: AnyObject) {
delegate?.userDidEnterInformation(info: textField.text!)
_ = self.navigationController?.popViewController(animated: true)
}
}

最佳实践

  • 正向传递数据时,若使用Storyboards,推荐使用prepareForSegue方法;若不使用Storyboards,可直接设置属性。
  • 反向传递数据时,若数据传递逻辑简单,可使用Block;若逻辑复杂,推荐使用协议和代理。
  • 使用NSNotificationCenter时,注意在合适的时机移除观察者,避免内存泄漏。

常见问题

代理方法未调用

可能是未正确设置代理,需确保在创建目标视图控制器时,将源视图控制器设置为其代理。

通知未收到

可能是通知名称拼写错误,或未正确注册观察者。需检查通知名称和注册代码。


在视图控制器之间传递数据
https://119291.xyz/posts/passing-data-between-view-controllers/
作者
ww
发布于
2025年6月19日
许可协议