prepareForSegue 在 Button 动作之前执行
prepareForSegue Being performed before Button action
我是 IOS 的新手,正在尝试解决这个问题。
我正在尝试将 json 数据从一个视图控制器传递到另一个(TableViewcontroller),我希望在 prepareForSegue 方法(然后将我从按钮操作获得的 json 数据传递给 TableViewcontroller。
但是我的 preparesegue 方法是在按钮操作之前先执行的,而 tableview 没有数据?
但是当我第二次单击该按钮时,我得到了表格视图 json 数据。
当我尝试将 Textfield 传递给 URl 时会发生这种情况,如果我使用直接 url 则不会。
有人可以帮我解决这个问题吗?
也让我知道是否有任何其他方法可以做到这一点。
这是第一个VC中的代码:
- (IBAction)buttonShow:(id)sender {
NSURLSession *session = [NSURLSession sharedSession];
NSString *urlString = [NSString stringWithFormat:@"Some API URL=%@",self.ingredientTextfield.text];
NSURL *url = [[NSURL alloc]initWithString:urlString];
req = [[NSMutableURLRequest alloc]initWithURL:url];
NSURLSessionDataTask *datatask = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *rcCount = [json objectForKey:@"count"];
NSLog(@"Recipe Count : %@",rcCount);
NSMutableDictionary *recipedata = [json objectForKey:@"recipes"];
NSLog(@"Recipe data : %@",recipedata);
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
NSString *title = [singlerecipedata objectForKey:@"title"];
NSLog(@"Recipe title : %@",title);
[titlearray addObject:title];
NSLog(@"tittlearray : %@",titlearray);
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
NSLog(@"tittle array inside for:%@",actualDataArray);
}];
NSLog(@"tittle array outside :%@",actualDataArray);
[datatask resume];
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:@"segueIdentifier"]) {
RecipesTableViewController *new = [segue destinationViewController];
NSLog(@"in segue :%@",actualDataArray);
[new setMyrecipeTittleArray:actualDataArray];
}
}
如果在IBAction buttonTapped:(id)sender方法中调用了performSegueWithIdentifier方法,则在调用buttonTapped之后必然会调用prepareForSegue。例如,看看这段代码:
#import "ViewController.h"
@interface ViewController ()
- (IBAction)goButtonTapped:(id)sender;
@property (weak, nonatomic) IBOutlet UIButton *buttonButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSLog(@"PrepareForSegue called");
}
- (IBAction)goButtonTapped:(id)sender {
NSLog(@"Button tapped %@", [sender description]);
[self performSegueWithIdentifier:@"SegueToTabs" sender:nil];
}
@end
这会在控制台中产生以下消息:
2016-04-05 21:41:46.300 GUITesting[67708:5496161] Button tapped <UIButton: 0x7fbd4411f010; frame = (20 62; 374 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fbd4411b2f0>>
2016-04-05 21:41:46.304 GUITesting[67708:5496161] prepareForSegue called
因此,也许您在代码中执行了其他操作,导致您的 prepareForSegue 在您的 buttonTapped 之前被调用。
顺便说一句,如果您想将 prepareForSegue 中的一些对象发送到下一个 VC,您可以简单地将其设置为 prepareForSegue 中目标 VC 的 属性方法。
在你的 For 循环之间放置 Prepare for Segue,然后使用 Breakpoints 你可以检查结果!!
您的代码必须是:
- (IBAction)buttonShow:(id)sender {
NSURLSession *session = [NSURLSession sharedSession];
NSString *urlString = [NSString stringWithFormat:@"Some API URL=%@",self.ingredientTextfield.text];
NSURL *url = [[NSURL alloc]initWithString:urlString];
req = [[NSMutableURLRequest alloc]initWithURL:url];
NSURLSessionDataTask *datatask = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *rcCount = [json objectForKey:@"count"];
NSLog(@"Recipe Count : %@",rcCount);
NSMutableDictionary *recipedata = [json objectForKey:@"recipes"];
NSLog(@"Recipe data : %@",recipedata);
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
NSString *title = [singlerecipedata objectForKey:@"title"];
NSLog(@"Recipe title : %@",title);
[titlearray addObject:title];
NSLog(@"tittlearray : %@",titlearray);
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
NSLog(@"tittle array inside for:%@",actualDataArray);
}];
NSLog(@"tittle array outside :%@",actualDataArray);
[datatask resume];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:@"segueIdentifier"]) {
RecipesTableViewController *new = [segue destinationViewController];
NSLog(@"in segue :%@",actualDataArray);
[new setMyrecipeTittleArray:actualDataArray];
}
}
发生这种情况是因为 NSURLSessionDataTask
是 异步的 。因此,将不会在您调用时检索数据:
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
为了更清楚地说明事情,这是事件的顺序:
- "Start the HTTP request":
[datatask resume];
- "Perform segue":
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
然后您的数据会在稍后的某个时候下载到 callback/completion 块中:
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
// ...
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
...此时您的 segue 已经执行。
要解决此问题,您可以执行以下两项操作之一:
我的首选方法,因为它感觉 对用户来说更快(尽管这实现起来更具挑战性):
- 在您的主视图控制器上实施委托协议和方法,在数据任务完成时通知 child (table)
- 实施某种加载指示器(例如
UIActivityIndicator
)
- 使 child (table) 成为主人的代表
- 调用委托方法时,让 child 自行更新(例如
[tableView reloadData]
)
这有利于立即执行 segue,这对用户来说感觉更快,并且在可用时异步 (non-blockingly) 呈现数据。您甚至可以非常灵活地执行一些错误检查并在数据加载失败或网络超时时显示 out-of-the-way 通知。
另一种方式,更容易实现,但有一些主要缺点:
将您的 performSegue
调用移到数据任务的完成块中:
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
// ...
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
这样做最大的缺点就是用户会觉得慢。即使连接良好,现在在按下按钮和执行 segue 之间可能会有 100-500 毫秒的延迟。在慢速或无响应的网络连接上,可能有几秒钟不会执行 segue。这让用户感到沮丧。
我是 IOS 的新手,正在尝试解决这个问题。
我正在尝试将 json 数据从一个视图控制器传递到另一个(TableViewcontroller),我希望在 prepareForSegue 方法(然后将我从按钮操作获得的 json 数据传递给 TableViewcontroller。
但是我的 preparesegue 方法是在按钮操作之前先执行的,而 tableview 没有数据?
但是当我第二次单击该按钮时,我得到了表格视图 json 数据。
当我尝试将 Textfield 传递给 URl 时会发生这种情况,如果我使用直接 url 则不会。
有人可以帮我解决这个问题吗? 也让我知道是否有任何其他方法可以做到这一点。
这是第一个VC中的代码:
- (IBAction)buttonShow:(id)sender {
NSURLSession *session = [NSURLSession sharedSession];
NSString *urlString = [NSString stringWithFormat:@"Some API URL=%@",self.ingredientTextfield.text];
NSURL *url = [[NSURL alloc]initWithString:urlString];
req = [[NSMutableURLRequest alloc]initWithURL:url];
NSURLSessionDataTask *datatask = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *rcCount = [json objectForKey:@"count"];
NSLog(@"Recipe Count : %@",rcCount);
NSMutableDictionary *recipedata = [json objectForKey:@"recipes"];
NSLog(@"Recipe data : %@",recipedata);
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
NSString *title = [singlerecipedata objectForKey:@"title"];
NSLog(@"Recipe title : %@",title);
[titlearray addObject:title];
NSLog(@"tittlearray : %@",titlearray);
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
NSLog(@"tittle array inside for:%@",actualDataArray);
}];
NSLog(@"tittle array outside :%@",actualDataArray);
[datatask resume];
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:@"segueIdentifier"]) {
RecipesTableViewController *new = [segue destinationViewController];
NSLog(@"in segue :%@",actualDataArray);
[new setMyrecipeTittleArray:actualDataArray];
}
}
如果在IBAction buttonTapped:(id)sender方法中调用了performSegueWithIdentifier方法,则在调用buttonTapped之后必然会调用prepareForSegue。例如,看看这段代码:
#import "ViewController.h"
@interface ViewController ()
- (IBAction)goButtonTapped:(id)sender;
@property (weak, nonatomic) IBOutlet UIButton *buttonButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSLog(@"PrepareForSegue called");
}
- (IBAction)goButtonTapped:(id)sender {
NSLog(@"Button tapped %@", [sender description]);
[self performSegueWithIdentifier:@"SegueToTabs" sender:nil];
}
@end
这会在控制台中产生以下消息:
2016-04-05 21:41:46.300 GUITesting[67708:5496161] Button tapped <UIButton: 0x7fbd4411f010; frame = (20 62; 374 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fbd4411b2f0>>
2016-04-05 21:41:46.304 GUITesting[67708:5496161] prepareForSegue called
因此,也许您在代码中执行了其他操作,导致您的 prepareForSegue 在您的 buttonTapped 之前被调用。
顺便说一句,如果您想将 prepareForSegue 中的一些对象发送到下一个 VC,您可以简单地将其设置为 prepareForSegue 中目标 VC 的 属性方法。
在你的 For 循环之间放置 Prepare for Segue,然后使用 Breakpoints 你可以检查结果!!
您的代码必须是:
- (IBAction)buttonShow:(id)sender {
NSURLSession *session = [NSURLSession sharedSession];
NSString *urlString = [NSString stringWithFormat:@"Some API URL=%@",self.ingredientTextfield.text];
NSURL *url = [[NSURL alloc]initWithString:urlString];
req = [[NSMutableURLRequest alloc]initWithURL:url];
NSURLSessionDataTask *datatask = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *rcCount = [json objectForKey:@"count"];
NSLog(@"Recipe Count : %@",rcCount);
NSMutableDictionary *recipedata = [json objectForKey:@"recipes"];
NSLog(@"Recipe data : %@",recipedata);
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
NSString *title = [singlerecipedata objectForKey:@"title"];
NSLog(@"Recipe title : %@",title);
[titlearray addObject:title];
NSLog(@"tittlearray : %@",titlearray);
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
NSLog(@"tittle array inside for:%@",actualDataArray);
}];
NSLog(@"tittle array outside :%@",actualDataArray);
[datatask resume];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:@"segueIdentifier"]) {
RecipesTableViewController *new = [segue destinationViewController];
NSLog(@"in segue :%@",actualDataArray);
[new setMyrecipeTittleArray:actualDataArray];
}
}
发生这种情况是因为 NSURLSessionDataTask
是 异步的 。因此,将不会在您调用时检索数据:
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
为了更清楚地说明事情,这是事件的顺序:
- "Start the HTTP request":
[datatask resume];
- "Perform segue":
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
然后您的数据会在稍后的某个时候下载到 callback/completion 块中:
NSMutableArray *titlearray = [[NSMutableArray alloc]init]; for (NSMutableDictionary *singlerecipedata in recipedata) { // ... } actualDataArray = [[NSArray alloc]initWithArray:titlearray];
...此时您的 segue 已经执行。
要解决此问题,您可以执行以下两项操作之一:
我的首选方法,因为它感觉 对用户来说更快(尽管这实现起来更具挑战性):
- 在您的主视图控制器上实施委托协议和方法,在数据任务完成时通知 child (table)
- 实施某种加载指示器(例如
UIActivityIndicator
) - 使 child (table) 成为主人的代表
- 调用委托方法时,让 child 自行更新(例如
[tableView reloadData]
)
这有利于立即执行 segue,这对用户来说感觉更快,并且在可用时异步 (non-blockingly) 呈现数据。您甚至可以非常灵活地执行一些错误检查并在数据加载失败或网络超时时显示 out-of-the-way 通知。
另一种方式,更容易实现,但有一些主要缺点:
将您的 performSegue
调用移到数据任务的完成块中:
NSMutableArray *titlearray = [[NSMutableArray alloc]init];
for (NSMutableDictionary *singlerecipedata in recipedata) {
// ...
}
actualDataArray = [[NSArray alloc]initWithArray:titlearray];
[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
这样做最大的缺点就是用户会觉得慢。即使连接良好,现在在按下按钮和执行 segue 之间可能会有 100-500 毫秒的延迟。在慢速或无响应的网络连接上,可能有几秒钟不会执行 segue。这让用户感到沮丧。