On the iPad, one can show a UIActionSheet using -showFromBarButtonItem:an开发者_C百科imated:. This is convenient because it wraps a UIPopoverController around the action sheet and it points the popover's arrow to the UIBarButtonItem that is passed in.
However, this call adds the UIBarButtomItem's toolbar to the list of passthrough views - which isn't always desirable. And, without a pointer to the UIPopoverController, one can't add other views to the passthrough list.
Does anyone know of a sanctioned approach to getting a pointer to the popover controller?
Thanks in advance.
You would need to adjust on orientation change.
I have found an alternative solution, that works perfectly for me.
Stick to
- (void)showFromBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated
In your @interface add
UIActionSheet *popoverActionsheet;
also add
@property (nonatomic, retain) UIActionSheet *popoverActionsheet;
also add
- (IBAction)barButtonItemAction:(id)sender;
So you have reference to your actionsheet from anywhere in your implementation.
in your implementation
- (IBAction) barButtonItemAction:(id)sender
{
//If the actionsheet is visible it is dismissed, if it not visible a new one is created.
if ([popoverActionsheet isVisible]) {
[popoverActionsheet dismissWithClickedButtonIndex:[popoverActionsheet cancelButtonIndex]
animated:YES];
return;
}
popoverActionsheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:@"Save Page", @"View in Safari", nil];
[popoverActionsheet showFromBarButtonItem:sender animated:YES];
}
in your actionsheet delegate implement
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == [actionSheet cancelButtonIndex]) return;
//add rest of the code for other button indeces
}
and don't forget to release popoverActionsheet in
- (void)dealloc
I trust that this will do.
if ([[[[actionSheet superview] superview] nextResponder] respondsToSelector:@selector(setPassthroughViews:)]) {
[[[[actionSheet superview] superview] nextResponder] performSelector:@selector(setPassthroughViews:) withObject:nil];
}
Will do the trick, this shouldn't cause any problems with the App Reviewers either as its not calling any private APIs.
It is fragile - the if statements ensures your code won't crash (just not work) in the unlikely event Apple change the underlying implementation.
I doubt there's a sanctioned approach to this since actionsheets are presented in UIPopoverViews which is linked to a UIViewController not a UIPopoverController
NSLog(@"%@",[[[popoverActionsheet superview] superview] passthroughViews]);
Looking through the UIPopoverView header file (UNDOCUMENTED) yields the following solution albeit undocumented. Doubt you can snick that past the App Reviewers but give it a go if you think it's worth a shot.
http://github.com/kennytm/iphone-private-frameworks/blob/master/UIKit/UIPopoverView.h
NSArray *passthroughViews = [[[popoverActionsheet superview] superview] passthroughViews];
NSMutableArray *views = [passthroughViews mutableCopy];
[views addObject:[self view]];
[[[popoverActionsheet superview] superview] setPassthroughViews:views];
[views release];
I don't think that's feasible. I was sitting with the same problem.
Use
- (void)showFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated
Example:
[actionSheet showFromRect:[[[myToolBar subviews] objectAtIndex:0] frame] inView:myToolBar animated:YES];
Note: You must change the index for objectAtIndex: to fit your situation and have reference to your ToolBar
Here is the solution. I just discovered it and it's so easy. Set userInteractionEnabled to NO before showing the action sheet and set it to YES at the end of your action sheet delegate method for dismissal.
- (void)promptResetDefaults:(id)sender
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:NSLocalizedString(@"Reset All Defaults", nil)
otherButtonTitles:nil];
self.navigationController.navigationBar.userInteractionEnabled = NO;
[actionSheet showFromBarButtonItem:sender animated:YES];
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == actionSheet.destructiveButtonIndex)
{
[self.dataDefaults resetDataDefaults];
[self.tableView reloadData];
}
self.navigationController.navigationBar.userInteractionEnabled = YES;
}
I've been using nbransby's answer for a couple years, but that no longer works in iOS 8. However, the problem still exists with the new UIAlertController
in iOS 8. Here's an update that works for me:
UIAlertController *actionSheet = [UIAlertController
alertControllerWithTitle:@"Test"
message:@"Hello, world!"
preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {}]];
actionSheet.modalPresentationStyle = UIModalPresentationPopover;
actionSheet.popoverPresentationController.barButtonItem = testButton;
[self presentViewController:actionSheet animated:TRUE completion:^(void) {
actionSheet.popoverPresentationController.passthroughViews = [NSArray array];
}];
Note that passthroughViews has to be set after the popover appears, but you can conveniently use the completion block to do that.
When presenting from a bar button item, you usually want to disable all other buttons on your toolbar until the action sheet is dismissed.
I think the simplest way to do this is to modify the UIToolbar class. You'll need a way to get hold of the actionsheet, perhaps by saving it in the app delegate.
Make a file UIToolbar+Modal.m, like this:
@implementation UIToolbar (Modal)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
UIActionSheet *mySheet = [[[UIApplication sharedApplication] delegate] myActionSheet];
if ([mySheet isVisible]) return nil;
else return hitView;
}
@end
The .h file doesn't need to contain anything special
@interface UIToolbar (Modal) {
}
@end
By the way, before finding this solution I tried assigning an empty array to passthroughViews, that you can access as (originally) described by stalinkay, but this doesn't in fact work (in addition to being undocumented).
The other ways to do this all have disadvantages - either you have to handle orientation changes, or the toolbar buttons look like they press down when in fact the only action taken is to dismiss the actionsheet.
UPDATE
As of iOS 4.3 this no longer works. You need to subclass:
UIToolbarWithModal.h
#import <Foundation/Foundation.h>
@interface UIToolbarWithModal : UIToolbar {
}
@end
Remember when you create the action sheet you need to keep it accessible, perhaps in your app delegate - in this example in the myActionSheet property.
UIToolbarWithModal.m
#import "UIToolbarWithModal.h"
#import "MyDelegate.h"
@implementation UIToolbarWithModal
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
UIActionSheet *mySheet = [[[UIApplication sharedApplication] delegate] myActionSheet];
if ([mySheet isVisible]) return nil;
else return hitView;
}
@end
Then just use UIToolbarWithModal in your code where you would have used UIToolbar before.
精彩评论