JimZ
JimZ

Reputation: 1202

How to handle concurrency by queue/NSThread for UI design, iOS dev

What I am trying to achieve is simple, from first thinking though. I found it hard to handle at last.

I would like to push a table view as a selection list, user select one cell and the cell string was sent to the previous view as selected string, simple huh??

See two pictures first

select housing, then push to the next view

select one cell in this view

what bothers me is that:

I would like to provide (at least) two buttons, one on the left is back button auto-generated by navigation controller, and the right one is for editing. And the navigation controller is defaulted to have two buttons (from my knowledge). So there is no place for "Done" button, which is supposed for user to tap and then confirm and pop to the previous view. So, when the user tap a cell, "Wearing" for example, I would like the following to happen, automatically and visually SEEable for user:

  1. user can SEE that "Housing" cell is unmarked
  2. then user can SEE that "Wearing" cell is marked
  3. then after a little time gap (say 0.2 second), pop to the previous view and update the selection, automatically.

At first I thought it's easy but it's definitely not. Here is my code for doing it, but working wired

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0ul); dispatch_async(queue, ^{ //unmark previous cell if (selectedIndexPath!=nil) { [[self.tableView cellForRowAtIndexPath:selectedIndexPath]setAccessoryType:UITableViewCellAccessoryNone]; } selectedIndexPath=indexPath; //get the selected cell to mark UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath]; [cell setAccessoryType:UITableViewCellAccessoryCheckmark]; dispatch_sync(dispatch_get_main_queue(), ^{ //wait a little [NSThread sleepForTimeInterval:0.2]; //return to previous view NSLog(@"here........."); if ([objectToUpdateCategory respondsToSelector:@selector(updateCategoryTo:withSelectedIndexPath:)]) { NSLog(@"sending.......... update info"); [objectToUpdateCategory updateCategoryTo:cell.textLabel.text withSelectedIndexPath:selectedIndexPath]; NSLog(@"sent update info"); } [self.navigationController popViewControllerAnimated:YES]; }); });

The tricky thing is that if I put [self.navigationController popViewControllerAnimated:YES]; to the last, the view will not visually update the unmark and mark step and go back to the previous view immediately. At first, when I didn't consider the unmark thing, the “queue" stuff in code can do the mark step visually before popping back, but sometimes not working. I don't know if my code is correct, actually I don't quite understand this queue tech from apple. But I'm pretty sure it has something to do with NSThread / queue or else that handle concurrency. I've checking Apple documents for a whole day and found no direct answer.

Hope someone could help me on this, thanks in advance :)

Upvotes: 0

Views: 356

Answers (2)

bobnoble
bobnoble

Reputation: 5824

To "after a little time gap (say 0.2 second), pop to the previous view", use the performSelector:withObject:afterDelay: methods or one of its variants, e.g.:

[self performSelector:@selector(delayedPop) withObject:nil afterDelay:0.2];

and put the popViewControllerAnimated in the delayedPop method, e.g.:

-(void)delayedPop{
    [self.navigationController popViewControllerAnimated:YES];
}

Upvotes: 1

Marcin Kuptel
Marcin Kuptel

Reputation: 2664

First of all, as I wrote in my comment, you shouldn't update the UI on a background thread. This will cause a lot of problems, including the UI not being updated immediately. In your case you don't need to use dispatch_async or dispatch_sync at all. What I would do is create a property in the view controller that displays the categories table view:

@property (nonatomic, weak) id<CategoryControllerDelegate> delegate;

When you push the category controller on the stack you set your expense controller as the delegate. Then, when the user makes a selection in the category controller, you call a method on the delegate (defined in a protocol), for example the one in your code sample:

@protocol CategoryControllerDelegate<NSObject>
@optional
- (void) updateCategoryTo: (NSString*) category withSelectedIndexPath: (NSIndexPath*) path;
@end

After that you pop the current view controller off the stack.

Upvotes: 0

Related Questions