Bronze challenge : dismiss popover?


#1

Hi,

I could show the assetTypePicker in a popover with the following code :

In DetailViewController.m

[code]- (IBAction)showAssetTypePicker:(id)sender
{

if ([imagePickerPopover isPopoverVisible]) {
    // If the popover is already up, get rid of it
    [assetTypePickerPopover dismissPopoverAnimated:YES];
    assetTypePickerPopover = nil;
    return;
}


[[self view] endEditing:YES];

AssetTypePicker *assetTypePicker = [[AssetTypePicker alloc]init];
[assetTypePicker setItem:item];


if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    // Create a new popover controller that will display the assetTypePicker
    assetTypePickerPopover = [[UIPopoverController alloc] initWithContentViewController:assetTypePicker];
    
    [assetTypePickerPopover setDelegate:self];
    
    [assetTypePickerPopover setPopoverContentSize:CGSizeMake(300,200) animated:YES];

    
        
    // Display the popover controller; sender
    // is the frame of the assetTypeButton
    [assetTypePickerPopover presentPopoverFromRect:[sender frame]
                                            inView:self.view
                          permittedArrowDirections:UIPopoverArrowDirectionAny
                                               animated:YES];

} else {
    [[self navigationController] pushViewController:assetTypePicker
                                           animated:YES];
}

}
[/code]

However, I can’t find a way to dismiss the popover when I select an assetType.
Must be a way in the assetTypePicker.m, method tableView:didSelectRowAtIndexPath: to tell the DetailViewController to dismiss the popover and reload the view.

If someone could help me get back on track, I’d greatly appreciate !

Thanks
Fred


#2

I finally found a way using NSNotificationCenter :
http://stackoverflow.com/questions/4937270/how-to-setup-popover-views-to-dismiss-properly

Here is how I implemented it :

In DetailViewController.m

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
    NSLog(@"User dismissed popover");
    imagePickerPopover = nil;
    assetTypePickerPopover = nil;

}

- (IBAction)showAssetTypePicker:(id)sender
{
    
    if ([imagePickerPopover isPopoverVisible]) {
        // If the popover is already up, get rid of it
        [assetTypePickerPopover dismissPopoverAnimated:YES];
        assetTypePickerPopover = nil;
        return;
    }

    
    [[self view] endEditing:YES];
    
    AssetTypePicker *assetTypePicker = [[AssetTypePicker alloc]init];
    [assetTypePicker setItem:item];
    
    
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        // Create a new popover controller that will display the assetTypePicker
        assetTypePickerPopover = [[UIPopoverController alloc] initWithContentViewController:assetTypePicker];
        
        [assetTypePickerPopover setDelegate:self];
        
        [assetTypePickerPopover setPopoverContentSize:CGSizeMake(300,200) animated:YES];
    
        // Listen for the notification which will be posted
        // by tapping in the content view controller's tableview.
        // When the notification is received, 
        // call the DidChooseRowInPopover: method...
        [[NSNotificationCenter defaultCenter] 
         addObserver:self
         selector:@selector(didChooseRowInPopover:)
         name:@"didChooseRowInPopover" 
         object:assetTypePickerPopover.contentViewController];
            
        // Display the popover controller; sender
        // is the frame of the assetTypeButton
        [assetTypePickerPopover presentPopoverFromRect:[sender frame]
                                                inView:self.view
                              permittedArrowDirections:UIPopoverArrowDirectionAny
                                                   animated:YES];

    } else {
        [[self navigationController] pushViewController:assetTypePicker
                                               animated:YES];
    }
}


- (void)didChooseRowInPopover:(NSNotification *)notification
{
    // Row in content view controller was tapped, dismiss popover
    // and update assetTypeButton label
    [assetTypePickerPopover dismissPopoverAnimated:YES];
    
    // I had to retype this from method viewWillAppear , don't know 
    // if there's a more elegant way to update the button label
    NSString *typeLabel = [[item assetType] valueForKey:@"label"];
    if (!typeLabel)
        typeLabel = @"None";
    
    
    [assetTypeButton setTitle:[NSString stringWithFormat:@"Type: %@", typeLabel]
                     forState:UIControlStateNormal];

}

And in AssetTypePicker.m

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:ip];
    
    [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
    
    NSArray *allAssets = [[BNRItemStore sharedStore] allAssetTypes];
    NSManagedObject *assetType = [allAssets objectAtIndex:[ip row]];
    [item setAssetType:assetType];
    
    
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
    {
        // Send a notification for notification center
        [[NSNotificationCenter defaultCenter] 
         postNotificationName:@"didChooseRowInPopover" object:self];


    } else {
        [[self navigationController] popViewControllerAnimated:YES];

    }
}

It works OK, but I wonder if other techniques that we know at that point in the book are possible … (I’m thinking about a performSelector …)
Any suggestions / enhancements to this code are welcome !

Thanks
Fred


#3

Thanks so much for this. This “bronze challenge” was for me the hardest of the three (for me silver should be bronze, gold should be silver, and bronze should be gold). :slight_smile:

Like you, I, too, really wanted to dismiss the popover once the row was selected in the AssetTypePickerController (essentially mimicking the behavior on the iPhone). However, your code didn’t work for me entirely; namely, I had to use this line of code in the didChooseRowInPopover method:

Otherwise, I got the old item asset type label, not the new one.

The only other aberration is that sometimes the item’s new assetType isn’t saved. Most of the time it is, sometimes it’s not. Not sure what that is about, but I’m ready to move on. If I do anything more with this it will be trying to figure out if incorporating a block into the notification is helpful: http://developer.apple.com/library/ios/ipad/#featuredarticles/Short_Practical_Guide_Blocks/_index.html


#4

Also, in the didChooseRowInPopover: method, I added:


#5

I sent the popover controller to the assetTypePicker and dismissed the popover from within the assetTypePicker which is simple and works but doesn’t seem very OO. I also then had to pass the assetType Label into the assetTypePicker as the value of the item was being updated but the detail item view wasn’t being reloaded when the popover is dismissed.
e.g.
//pc and assetTypeButton are references to the original popover view controller and UIbutton in DetailItemView
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad )
{
NSString *typeLabel = [assetType valueForKey:@“label”];
[assetTypeButton setTitle:[NSString stringWithFormat:@“Type: %@”, typeLabel] forState:UIControlStateNormal];

    [pc dismissPopoverAnimated:YES];

} else {
    [[self navigationController] popViewControllerAnimated:YES];
}

#6

I just thought I’d throw in my tidbit on how I accomplished having the popover disappear since I don’t see anyone else mentioning it :wink:. It actually seems a little easier then signing up to the notification center… then again maybe not. Does anyone disagree?
In AssetTypePicker.m:

[code]-(IBAction)dismissPopover:(id)sender{
NSString *selector = NSStringFromSelector(_cmd);
SEL newSelector = NSSelectorFromString(selector);

if ([[self controller] respondsToSelector:newSelector]) {
    //Ignore warning for this line - may or may not appear, doesn't matter
    [[self controller] performSelector:newSelector withObject:sender];
}

}

[/code]

Then in DetailsViewController there’s a nice method in there I created to dismiss the Popover.


#7

I declare a property in AssetTypePicker.h

     @property (nonatomic, weak) DetailViewController *dvc;

and in DetailViewController.h I declare

    @property (nonatomic, strong) UIPopoverController *assetPickerPopover;

Then before present the popover. Assign DetailViewController to AssetTypePicker:

    [assetTypePicker setDvc:self];

now you can dismiss the popover in - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip

    [[dvc assetPickerPopover] dismissPopoverAnimated:YES];

#8

I achieved dismissing the popover after selecting something in it like this:

  1. Added the line
@property (weak, nonatomic) id popoverController;

to the AssetTypePicker.h file
2. In showAssetTypePicker method of DetailViewController i added the following

[myAssetTypePicker setPopoverController: assetPickerPopover];

to send the view controller to the AssetType instance
3. And finally, in the AssetTypePicker.m in the (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip method I added the line:

[popoverController dismissPopoverAnimated:YES];

Looks like it does the job.


#9

I used a solution similar to titicaca to dismiss the popover, but I also passed a pointer of the DetailViewController to AssetTypePicker so that upon dimiss it would run a new function I wrote called (void)updateAssetTypeButton. This way when the popover dismissed the assetTypeButton had the appropriate txt on there. The code was reused from the viewWillAppear and looks like:

-(void)updateAssetTypeButton
{
    NSString *typeLabel = [[item assetType] valueForKey:@"label"];
    if (!typeLabel) {
        typeLabel = @"None";
    }
    [assetTypeButton setTitle:[NSString stringWithFormat:@"Type: %@", typeLabel] forState:UIControlStateNormal];
}

#10

Tiberius, great point to do it.
I used almost the same - I passed controller of DetailViewController too and then ran - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
method that we already have from AssetTypePicker.


#11

I too am puzzled by the level of dificulty with this Bronze Challenge. This has actually happened to me several times with the BSG where Bronze turned out to be the hardest. Anyway, I don’t think we should have to use NSNotification. That seems too artificial. Not saying it isn’t. It just doesn’t feel right.

I was trying to go along the lines of the other solutions. I wanted to just import the header file for DetailViewController which held and instance variable, publicly available, for the assetPopover. I thought by doing so I could then simply call dismissPopover:Animated:. But, my assetPicker implementation would never see the instance variable. Any one have a reason why this might be?


#12

Better solution.

I added a property for a popover in the AssetPicker h & m. Then set it’s property from detailViewController. Then I was able to call dismissPopover:animated:

The only issue I’m having now is automatically updating the text of the assetPicker button. Even though I imported DetailViewController into AssetPicker I still can’t access the instance variables or properties.


#13

Might be I totally didn’t get the point but I tried the following solution staying in line with the previous chapter using a selector.
detailviewcontroller:

[code]- (IBAction)showAssetTypePicker:(id)sender {
// [[self view] endEditing:YES];

AssetTypePicker *assetTypePicker = [[AssetTypePicker alloc] init];
[assetTypePicker setItem:item];

/* for Bronze purpose comment this part
[[self navigationController] pushViewController:assetTypePicker
animated:YES];

setup popover instead of navigationcontroller doing this

*/
NSString *selector = NSStringFromSelector(_cmd);
selector = [selector stringByAppendingString:@“withAssetTypePicker:”];
SEL newSelector = NSSelectorFromString(selector);

    if([controller respondsToSelector:newSelector]) {
        NSLog(@"select method exists");
        [controller performSelector:newSelector withObject:sender
                         withObject:assetTypePicker];
    }
    else{
        NSLog(@"select method does not exist with controller %@ and selector: %@", [controller description],[selector description]);

    }

}
[/code]

So this invoked the itemsviewcontroller where I added the following method:

[code]// New

  • (void)showAssetTypePicker:(id)sender withAssetTypePicker: (AssetTypePicker *) picker
    {
    NSLog(@“Going to show the AssetTypePicker %@”, [picker description]);

    CGRect rect = [[self view] convertRect:[sender bounds] fromView:sender];

    // Present a 600x600 popover
    assetTypePicker = [[UIPopoverController alloc] initWithContentViewController:picker];
    [assetTypePicker setDelegate:self];
    [assetTypePicker setPopoverContentSize:CGSizeMake(600, 600)];
    [assetTypePicker presentPopoverFromRect:rect
    inView:[self view]
    permittedArrowDirections:UIPopoverArrowDirectionAny
    animated:YES];

}
[/code]

for dismissing the popover I used this and simply added the other popover:

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { [imagePopover dismissPopoverAnimated:YES]; imagePopover = nil; [assetTypePicker dismissPopoverAnimated:YES]; assetTypePicker = nil; }

It did work when creating the object, but at a second time the following error appeared:

I have no clue why this happens as it shows the assetTypePicker which is a subclass of UITableViewController and should contain a view.

Any help would be highly appreciated!

EDIT: it seems in none of the solutions I’ve seen someone uses the itemsviewcontroller so will try detailviewcontroller. I’m very curious thous what this error occured especially as it seems to work the first time.