2010-03-21 17 views

Respuesta

28

El método antes de iOS 5 es conseguir instancia compartida del UIMenuController, establecer el objetivo rect y ver y llamar -setMenuVisible:animated:. Recuerde implementar -canPerformAction:withSender: en su respondedor.


El método después de iOS 5 (previamente disponible como característica no documentada) es implementar estos 3 métodos en la fuente de datos (véase https://developer.apple.com/reference/uikit/uitableviewdelegate#1653389).

-(void)tableView:(UITableView*)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath*)indexPath withSender:(id)sender; 
-(BOOL)tableView:(UITableView*)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath*)indexPath withSender:(id)sender; 
-(BOOL)tableView:(UITableView*)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath*)indexPath; 
+0

Gracias! El método no documentado es exactamente lo que estaba buscando. El método "oficial" es bastante torpe. Esperemos que se documente pronto. No es ideal que no esté documentado, pero en este caso solo está implementando el protocolo, no llamando a un método, así que con suerte es seguro (er). –

+0

¿Por qué estos métodos todavía no están documentados? ¿Son algún tipo de método privado? Si lo son, es posible que su aplicación no sea aceptada por la tienda de aplicaciones. – dombesz

+0

Están documentados desde iOS 5.0 como parte del protocolo UITableViewDelegate: http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITableViewDelegate_Protocol/Reference/Reference.html. –

14

Su subclase UITableViewCell puede tener este aspecto

@interface MenuTableViewCell : UITableViewCell { 
} 
- (IBAction)copy:(id)sender; 
- (void)showMenu; 

@end 


@implementation MenuTableViewCell 

- (BOOL)canBecomeFirstResponder { 
    return YES; 
} 
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender 
{ 
    if (action == @selector(copy:)) { 
     return YES; 
    } 
    return NO; 
} 
- (IBAction)copy:(id)sender { 
} 
- (void)showMenu { 
    [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; 
    [self becomeFirstResponder]; 
    [[UIMenuController sharedMenuController] update]; 
    [[UIMenuController sharedMenuController] setTargetRect:CGRectZero inView:self]; 
    [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES]; 

} 

@end 

Y los métodos de delegado UITableView son como

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 

    static NSString *CellIdentifier = @"Cell"; 

    MenuTableViewCell *cell = (MenuTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[[MenuTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
    } 

    // Configure the cell. 
    return cell; 
} 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    MenuTableViewCell *cell = (MenuTableViewCell *)[tableView cellForRowAtIndexPath:indexPath]; 
    [cell showMenu]; 
} 
+0

Gracias por la respuesta. Esta es una buena manera de hacerlo "oficialmente", supongo, pero lo que no me gusta es que muestra la vista Copiar cuando está seleccionada, mientras que la aplicación de la libreta de direcciones muestra la vista de copia cuando se mantiene durante X segundos. Esto no es un problema con su solución, sino con la API. Muchas gracias por su ayuda. –

+0

Cuando intento esta solución, parece que funciona, pero después de que se muestra el menú, las llamadas posteriores a [self.tableView reloadData] no llaman a las devoluciones de llamada UITableViewDataSource: numberOfSections, etc.Parece que la llamada a becomeFirstResponder es el método que amortigua la recarga de tableView, pero se necesita la llamada becomeFirstResponder para mostrar el menú. Es difícil creer que algo más no esté sucediendo. Muy confundido. Tal vez empiece un nuevo proyecto para ver si puedo reproducir de forma aislada. – Daniel

+0

Reproduje el problema en un proyecto nuevo y simplificado. En mi UITableViewCell personalizado que había implementado: - (BOOL) resignFirstResponder porque pensé que este sería un buen lugar para borrar los elementos del menú personalizado desde sharedMenuController. Si no borra los elementos de menú personalizados, se adjuntan a otros UITextFields en la misma vista, donde no tienen sentido. En cualquier caso, incluso si mi resignFirstResponder no hace nada más que devolver SÍ, todavía causa que las siguientes llamadas [tableView reloadData] fallen. Esto es muy extraño para mi Supongo que buscaré otro lugar para borrar el menú. – Daniel

34

En la actualidad existe la interfaz oficial para la visualización de menús celulares UITableView en IOS 5. Ejemplo (desde el delegado de la mesa):

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return YES; 
} 

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 
{ 
    return (action == @selector(copy:)); 
} 

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 
{ 
    if (action == @selector(copy:)){ 
     UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 
     [[UIPasteboard generalPasteboard] setString:cell.textLabel.text]; 
    } 
} 

Intenté modificar el controlador compartido de UIMenuController para agregar mi propio elemento de menú, y pude agregarlo y obtener el mensaje canPerformAction, pero devolver YES no me ayudó; No pude hacer aparecer mi elemento de menú personalizado. Según mis experimentos, parece que solo se admiten Copiar, Cortar y Pegar. [EDIT Dado que esto se publicó, aprendí cómo agregar elementos de menú personalizados.]

Tenga en cuenta que esto funciona solo si se implementan los tres métodos de delegado.

+0

Disponible a partir de iOS 5.0. –

+4

Agregue esto debajo de NSLog para copiar al cartón UITableViewCell * cell = [self tableView: tableView cellForRowAtIndexPath: indexPath]; [[UIPasteboard generalPasteboard] setString: cell.detailTextLabel.text]; – malaki1974

+0

Sí, eso fue esencial, así que edité su respuesta con eso :-) – malhal

9
#pragma mark - COPY/PASTE Cell Text via Menu 

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return YES; 
} 

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 
{ 
    return (action == @selector(copy:)); 
} 

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 
{ 
    if (action == @selector(copy:)) 
    { 
     UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 
     UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard]; 
     [pasteBoard setString:cell.textLabel.text]; 
    } 
} 
13

Ésta es la sintaxis Swift para copiar detailTextLabel.

override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool { 
    return (tableView.cellForRow(at: indexPath)?.detailTextLabel?.text) != nil 
} 

override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool { 
    return action == #selector(copy(_:)) 
} 

override func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) { 
    if action == #selector(copy(_:)) { 
     let cell = tableView.cellForRow(at: indexPath) 
     let pasteboard = UIPasteboard.general 
     pasteboard.string = cell?.detailTextLabel?.text 
    } 
} 
Cuestiones relacionadas