web-dev-qa-db-fra.com

Est-il possible d'effectuer manuellement un Popover Segue (à partir d'une cellule UITableView dynamique)?

J'ai besoin d'effectuer une séquence Popover lorsque l'utilisateur touche une cellule dans un tableau dynamique. Mais quand j'essaie de faire cela avec ce code:

- (void)tableView:(UITableView *)tableview didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
        [self performSegueWithIdentifier:@"toThePopover" sender:[tableView cellForRowAtIndexPath]];
    //...
} 

que je reçois une erreur:

Configuration illégale 

Popover Segue sans ancre

Y a-t-il un moyen de faire cela (effectuer une transition popover à partir de TableView dynamique manuellement)?

30
Hlib Dmytriiev

J'ai été confronté au même problème ce soir, avec quelques solutions de contournement (y compris la présentation de la popover à l'ancienne).

Pour cet exemple, un objet est stocké dans ma classe de cellule personnalisée. Lorsque la cellule est sélectionnée, j'appelle une fonction comme celle-ci pour ouvrir les détails d'un objet popOverViewController et pointer (ancrer) sa cellule correspondante dans la table.

    - (void)openCustomPopOverForIndexPath:(NSIndexPath *)indexPath{
        CustomViewController* customView = [[self storyboard] instantiateViewControllerWithIdentifier:@"CustomViewController"];

        self.myPopOver = [[UIPopoverController alloc]
                                   initWithContentViewController:customView];
        self.myPopOver.delegate = self;
        //Get the cell from your table that presents the popover
        MyCell *myCell = (MyCell*)[self.tableView cellForRowAtIndexPath:indexPath];
        CGRect displayFrom = CGRectMake(myCell.frame.Origin.x + myCell.frame.size.width, myCell.center.y + self.tableView.frame.Origin.y - self.tableView.contentOffset.y, 1, 1);
        [self.myPopOver presentPopoverFromRect:displayFrom
                                             inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
    }

Le problème avec cette méthode est que nous avons souvent besoin que la vue popover ait un initialiseur personnalisé. Cela pose problème si vous souhaitez que votre vue soit conçue dans Storyboard au lieu d'un xib et que vous disposiez d'une méthode init personnalisée qui prend l'objet associé à vos cellules comme paramètre à utiliser pour son affichage. Vous ne pouvez pas non plus utiliser une transition popover (à première vue) car vous avez besoin d'un point d'ancrage dynamique (et vous ne pouvez pas ancrer un prototype de cellule). Alors voici ce que j'ai fait:

  1. Tout d’abord, créez un UIButton 1px X 1px caché dans la vue Contrôleurs de vue. (important de donner au bouton des contraintes lui permettant d'être déplacé n'importe où dans la vue)
  2. Créez ensuite un point de vente pour le bouton (j'ai appelé le mien popOverAnchorButton) dans votre contrôleur de vue et faites glisser une séquence du bouton caché vers le contrôleur de vue auquel vous souhaitez vous soumettre. Faites-en une série pop-over.

Vous avez maintenant une transition populaire avec une ancre «légale». Le bouton est caché, ainsi personne ne peut le toucher accidentellement. Vous ne l'utilisez que pour un point d'ancrage.

Maintenant, appelez votre compte manuellement dans votre fonction comme ceci.

    - (void)openCustomPopOverForIndexPath:(NSIndexPath *)indexPath{
        //Get the cell from your table that presents the popover
        MyCell *myCell = (MyCell*)[self.tableView cellForRowAtIndexPath:indexPath];

        //Make the rect you want the popover to point at.
        CGRect displayFrom = CGRectMake(myCell.frame.Origin.x + myCell.frame.size.width, myCell.center.y + self.tableView.frame.Origin.y - self.tableView.contentOffset.y, 1, 1);

        //Now move your anchor button to this location (again, make sure you made your constraints allow this)
        self.popOverAnchorButton.frame = displayFrom;
        [self performSegueWithIdentifier:@"CustomPopoverSegue" sender:myCell];
    }

Et ...... voila. Maintenant, vous utilisez la magie des étapes avec toute leur grandeur et vous avez un point d'ancrage dynamique qui semble pointer vers votre cellule . Maintenant dans -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender vous pouvez simplement envoyer l'expéditeur à la classe de votre cellule (étant donné que vous faites le bon vérifie le type d’expéditeur et le nom de la séquence appelée) et donne à l’objet de la cellule destinationViewController du segment.

Faites-moi savoir si cela aide, ou quelqu'un a des commentaires ou des améliorations.

62
johnrechd

Il suffit d’ajouter cette réponse comme moyen alternatif de présenter un popover à partir d’une cellule touchée, même s’il utilise du code plutôt qu’une séquence. C'est assez simple cependant et cela a fonctionné pour moi de iOS 4 à iOS 7:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:NO];

    //get the data of the row they clicked from the array
    Url* clickedRec = [self.resultsArray objectAtIndex:indexPath.row];

    //hide the popover in case it was already opened from a previous touch.    
    if (self.addNewPopover.popoverVisible) {
            [self.addNewPopover dismissPopoverAnimated:YES];
            return;
        }

    //instantiate a view controller from the storyboard
    AddUrlViewController *viewControllerForPopover =
    [self.storyboard instantiateViewControllerWithIdentifier:@"addUrlPopup"];

    //set myself as the delegate so I can respond to the cancel and save touches.
    viewControllerForPopover.delegate=self;
    //Tell the view controller that this is a record edit, not an add        
    viewControllerForPopover.addOrEdit = @"Edit";
    //Pass the record data to the view controller so it can fill in the controls            
    viewControllerForPopover.existingUrlRecord = clickedRec;

    UIPopoverController *popController = [[UIPopoverController alloc]
                                          initWithContentViewController:viewControllerForPopover];

    //keep a reference to the popover since I'm its delegate        
    self.addNewPopover = popController;

    //Get the cell that was clicked in the table. The popover's arrow will point to this cell since it was the one that was touched.
    UITableViewCell *clickedCell = [self.tableView cellForRowAtIndexPath:indexPath];

    //present the popover from this cell's frame.
    [self.addNewPopover presentPopoverFromRect:clickedCell.frame inView:self.myTableView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
3
James Toomey

Réponse rapide à l'aide de popoverPresentationController: à l'aide du storyboard, configurez le nouveau contrôleur de vue avec un ID de storyboard de popoverEdit.

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let fromRect:CGRect = self.tableView.rectForRowAtIndexPath(indexPath)

    let popoverVC = storyboard?.instantiateViewControllerWithIdentifier("popoverEdit") as! UIViewController
    popoverVC.modalPresentationStyle = .Popover
    presentViewController(popoverVC, animated: true, completion: nil)
    let popoverController = popoverVC.popoverPresentationController
    popoverController!.sourceView = self.view
    popoverController!.sourceRect = fromRect
    popoverController!.permittedArrowDirections = .Any

}
2
Drewster

Je l'ai fait de la manière la plus simple: 

  1. Faites en sorte que cette présentation Popover se déroule dans le Storyboard comme d'habitude, mais que vous la glissiez depuis ViewController
  2. sélectionner la vue d'ancrage comme vue de table 
  3. puis, dans la vue de tableau, le bouton de la cellule apparaît 

     private func presentCleaningDateDatePicker(from button: UIButton) {
        performSegue(withIdentifier: "Date Picker Popover Segue", sender: button)
    }
    
  4. et implémenter la méthode prepare (for segue) 

      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
        if let identifier = segue.identifier {
            switch identifier {
            case "Date Picker Popover Segue":
    
                if let vc = segue.destination as? DatePickerViewController {
                    if let ppc = vc.popoverPresentationController {
                        ppc.sourceView = sender as! UIButton
                        ppc.sourceRect = (sender as! UIButton).frame
                        ppc.delegate = vc
    
                        vc.minimumDate = Date()
                        vc.maximumDate = Date().addMonth(n: 3)
                        vc.delegate = self
                    }
                }
            default:
                break
            }
        }
    }
    
0
Michał Ziobro