Acceso a UIPopoverController para UIActionSheet en iPad
(7)
Aquí está la solución. Lo acabo de descubrir y es muy fácil. Establezca userInteractionEnabled en NO antes de mostrar la hoja de acción y configúrelo en SÍ al final de su método de delegado de hoja de acción para el despido.
- (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;
}
En el iPad, uno puede mostrar una UIActionSheet usando -showFromBarButtonItem: animated :. Esto es conveniente porque envuelve un UIPopoverController alrededor de la hoja de acción y apunta la flecha del popover al elemento UIBarButtonItem que se pasa.
Sin embargo, esta llamada agrega la barra de herramientas de UIBarButtomItem a la lista de vistas de acceso directo, lo que no siempre es deseable. Y, sin un puntero al UIPopoverController, no se pueden agregar otras vistas a la lista de acceso directo.
¿Alguien sabe de un enfoque autorizado para obtener un puntero al controlador de popover?
Gracias por adelantado.
Cuando se presenta desde un elemento del botón de la barra, normalmente desea desactivar todos los demás botones de la barra de herramientas hasta que se descarte la hoja de acción.
Creo que la forma más sencilla de hacer esto es modificar la clase UIToolbar. Necesitará una forma de obtener la hoja de acciones, tal vez guardándola en el delegado de la aplicación.
Haga un archivo UIToolbar + Modal.m, así:
@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
El archivo .h no necesita contener nada especial
@interface UIToolbar (Modal) {
}
@end
Por cierto, antes de encontrar esta solución, intenté asignar una matriz vacía a PassthroughViews, a las que puede acceder como (originalmente) describió Stalinkay, pero esto no funciona (además de no estar documentado).
Las otras formas de hacer esto tienen desventajas, ya sea que tenga que manejar los cambios de orientación, o los botones de la barra de herramientas parecen que se presionan hacia abajo cuando, de hecho, la única acción que se toma es descartar la hoja de acciones.
ACTUALIZAR
A partir de iOS 4.3 esto ya no funciona. Necesitas subclase:
UIToolbarWithModal.h
#import <Foundation/Foundation.h>
@interface UIToolbarWithModal : UIToolbar {
}
@end
Recuerde que cuando cree la hoja de acción debe mantenerla accesible, tal vez en su delegado de aplicación, en este ejemplo en la propiedad myActionSheet.
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
Luego simplemente use UIToolbarWithModal en su código en el que habría usado UIToolbar antes.
Dudo que haya un enfoque autorizado para esto, ya que las hojas de acción se presentan en UIPopoverViews que está vinculado a un UIViewController no a un UIPopoverController
NSLog(@"%@",[[[popoverActionsheet superview] superview] passthroughViews]);
Al examinar el archivo de encabezado UIPopoverView (UNDOCUMENTED) se obtiene la siguiente solución, aunque no documentada. Duda que puedas dejar atrás a los revisores de aplicaciones, pero dale una oportunidad si crees que vale la pena intentarlo.
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];
He estado usando la respuesta de nbransby durante un par de años, pero eso ya no funciona en iOS 8. Sin embargo, el problema todavía existe con el nuevo UIAlertController
en iOS 8. Aquí hay una actualización que me funciona:
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];
}];
Tenga en cuenta que passthroughViews se debe establecer después de que aparezca la ventana emergente, pero puede usar el bloque de finalización para hacerlo.
Necesitarías ajustar el cambio de orientación.
He encontrado una solución alternativa, que funciona perfectamente para mí.
Atenerse a
- (void)showFromBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated
En tu @interface agrega
UIActionSheet *popoverActionsheet;
tambien agrega
@property (nonatomic, retain) UIActionSheet *popoverActionsheet;
tambien agrega
- (IBAction)barButtonItemAction:(id)sender;
Así que tiene referencia a su hoja de acción desde cualquier lugar de su implementación.
en tu implementacion
- (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];
}
en su hoja de acción delegar implementar
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == [actionSheet cancelButtonIndex]) return;
//add rest of the code for other button indeces
}
y no olvides lanzar popoverActionsheet en
- (void)dealloc
Confío en que esto servirá.
No creo que sea factible. Estaba sentada con el mismo problema.
Utilizar
- (void)showFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated
Ejemplo:
[actionSheet showFromRect:[[[myToolBar subviews] objectAtIndex:0] frame] inView:myToolBar animated:YES];
Nota: debe cambiar el índice para objectAtIndex: para adaptarse a su situación y hacer referencia a su barra de herramientas
if ([[[[actionSheet superview] superview] nextResponder] respondsToSelector:@selector(setPassthroughViews:)]) {
[[[[actionSheet superview] superview] nextResponder] performSelector:@selector(setPassthroughViews:) withObject:nil];
}
Hará el truco, esto no debería causar ningún problema con los revisores de la aplicación, ya que no llama a ninguna API privada.
Es frágil: las declaraciones if aseguran que su código no se bloqueará (simplemente no funcionará) en el improbable caso de que Apple cambie la implementación subyacente.