【问题标题】:prepareForSegue Being performed before Button actionprepareForSegue 在 Button 动作之前执行
【发布时间】:2016-07-26 03:18:03
【问题描述】:

我是 IOS 新手,正在尝试解决这个问题。

我正在尝试将 json 数据从一个视图控制器传递到另一个(TableViewcontroller),我希望在 prepareForSegue 方法(然后传递我从按钮获得的 json 数据)之前执行按钮操作(这具有 json 数据)动作)到 TableViewcontroller。

但是我的 preparesegue 方法是在按钮操作和 tableview 没有数据之前先执行的?

但是当我第二次单击该按钮时,我得到了 tableview json 数据。

如果我使用直接 url,当我尝试将 Textfield 传递给 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];
    }

}

【问题讨论】:

  • 你能提供一些代码吗?

标签: ios json uitableview segue viewcontroller


【解决方案1】:

在你的 For 循环之间放置准备 Segue,然后使用断点你可以检查结果!

您的代码必须是:

- (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];
    }

}

【讨论】:

  • 谢谢@shikha kochar。但这给了我错误:NSInternalInconsistencyException',原因:'-[UIKeyboardTaskQueue waitUntilAllTask​​sAreFinished] 只能从主线程调用。使用 NSOperation 将 performSegueWithIdentifier 添加到主线程后,我能够填充表格视图,但按钮单击和更新之间存在延迟表格视图。
【解决方案2】:

这是因为NSURLSessionDataTask异步。因此,不会在您调用时检索数据:

[self performSegueWithIdentifier:@"segueIdentifier" sender:sender]; 

为了更清楚地解释事情,这是事件的顺序:

  1. “启动HTTP请求”:[datatask resume];
  2. “执行转场”:[self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
  3. 然后您的数据稍后会在回调/完成块中下载:

    NSMutableArray *titlearray = [[NSMutableArray alloc]init];
    for (NSMutableDictionary *singlerecipedata in recipedata) {
        // ...
    } 
    actualDataArray = [[NSArray alloc]initWithArray:titlearray];
    

...此时您的转场已经执行。

要解决此问题,您可以执行以下两项操作之一:

我的首选方法,因为它感觉对用户来说更快(尽管实施起来更具挑战性):

  1. 在您的主视图控制器上实现一个委托协议和方法,当数据任务完成时通知子(表)
  2. 实现某种加载指示器(例如UIActivityIndicator
  3. 让孩子(表)成为主人的代表
  4. 调用委托方法时,让子进程自行更新(例如[tableView reloadData]

这样做的好处是立即执行 segue,用户感觉更快,并且在可用时异步(非阻塞)呈现数据。当数据加载失败或网络超时时,您甚至可以非常灵活地实施一些错误检查并提供不碍事的通知。

另一种方式,更容易实现,但有一些主要缺点:

将您的 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 可能会在几秒钟内不执行。这让用户感到沮丧。

【讨论】:

    【解决方案3】:

    如果在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 方法中将它设置为 targetVC 的一个属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-16
      • 1970-01-01
      • 2011-11-24
      • 1970-01-01
      • 2021-04-05
      • 1970-01-01
      • 2013-02-15
      相关资源
      最近更新 更多