2016-04-05 3 views
0

Я новичок в IOS и пытаюсь понять это.prepareForSegue Выполняется перед действием кнопки

Я пытаюсь передать данные json из одного контроллера View в другой (TableViewcontroller), и я хочу, чтобы действие кнопки (это данные json) выполнялось до метода prepareForSegue (который затем передает данные json, которые я получил от действие кнопки) в TableViewcontroller.

Но мой метод prepareseue выполняется сначала до нажатия кнопки, а tableview не получает данных?

Но когда я нажимаю кнопку во второй раз, я получаю данные в формате 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]; 
    } 

} 
+2

Вы можете указать код? –

ответ

0

Это происходит потому, что NSURLSessionDataTask является асинхронный. Таким образом, данные не будут получены в тот момент, когда вы звоните:

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

Чтобы объяснить все более ясно, что это последовательность событий:

  1. «Запустить запрос HTTP «: [datatask resume];
  2. "Выполнение SEGUE": [self performSegueWithIdentifier:@"segueIdentifier" sender:sender];
  3. И тогда ваши данные загружены в какой-то момент позже, в блоке обратного вызова/завершения:

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

... в этот момент ваш segue уже выполнен.

Чтобы решить эту проблему, вы можете сделать одну из двух вещей:

Моего предпочтительного метода, так как он чувствует быстрее к пользователю (хотя это гораздо более сложным для реализации):

  1. Реализовать протокол и метод делегата на контроллере главного представления, который уведомляет дочерний элемент (таблицу), когда задача данных завершена.
  2. Внесите какой-то индикатор загрузки (например, UIActivityIndicator)
  3. Сделать дочерним (таблицей) делегата мастера
  4. Когда вызывается метод делегата, имейте сам обновление ребенка (например, [tableView reloadData])

Это имеет преимущество сразу выполняя SEGUE, который чувствует себя быстрее к пользователю, так и асинхронно (не blockingly) представляет данные, если они доступны. Вы даже можете быть супер slick и выполнять некоторую проверку ошибок и представлять уведомления вне очереди, когда данные не загружаются или сеть не работает.

Другой способ, который проще в реализации, но имеет ряд существенных недостатков:

Перемещайте performSegue вызов внутри завершения блока задания данных:

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

Самый большой недостаток это то, что он будет чувствовать себя медленным для пользователя. Даже при отличной связи теперь, возможно, существует задержка в 100-500 мс между нажатием кнопки и выполнением операции segue. При медленном или невосприимчивом сетевом соединении, segue может не выполняться в течение нескольких секунд. Это расстраивает пользователей.

0

Если метод performSegueWithIdentifier вызывается в IBAction buttonTapped: Метод (ID) отправителя, то prepareForSegue обязательно будет вызываться после buttonTapped называется. В качестве примера, посмотрите на этот код:

#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 называться перед вашей кнопкой.

Btw, если вы хотите отправить какой-либо объект вместе в prepareForSegue на следующий VC, вы можете просто установить его как свойство targetVC в методе prepareForSegue.

1

Место Подготовьте для Segue Между вашим циклом For Loop, затем используя контрольные точки вы можете проверить результат!

Ваш код должен быть:

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

} 
+0

Спасибо @ shikha кочар. Но это дало мне ошибку: NSInternalInconsistencyException ', reason:' - [UIKeyboardTaskQueue waitUntilAllTasksAreFinished] может вызываться только из основного потока. После добавления executeSegueWithIdentifier в mainthread с помощью NSOperation я могу заполнить таблицу, но есть отставание между нажатием кнопки и обновлением TableView. – Rios8