Можно ли выполнить Popover Segue вручную (из динамической ячейки UITableView)?

Мне нужно выполнить переход Popover, когда пользователь касается ячейки в динамическом TableView. Но когда я пытаюсь сделать это с помощью этого кода:

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

чем я получаю сообщение об ошибке:

Недопустимая конфигурация

Переход в виде всплывающего окна без якоря

Есть ли способ сделать это (выполнить всплывающее окно из динамического TableView вручную)?


person Hlib Dmytriiev    schedule 12.12.2012    source источник


Ответы (4)


Я столкнулся с этой же проблемой сегодня вечером, есть пара обходных путей (включая представление всплывающего окна по старинке).

В этом примере у меня есть объект, который хранится в моем пользовательском классе ячеек. Когда ячейка выбрана, я вызываю подобную функцию, чтобы открыть подробности в popOverViewController об объекте и указать (привязать) к соответствующей ячейке в таблице.

    - (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];
    }

Проблема с этим методом заключается в том, что нам часто нужно, чтобы всплывающее окно имело собственный инициализатор. Это проблематично, если вы хотите, чтобы ваше представление было разработано в раскадровке вместо xib и имело собственный метод инициализации, который принимает объект, связанный с вашими ячейками, в качестве параметра для его отображения. Вы также не можете просто использовать всплывающий переход (на первый взгляд), потому что вам нужна динамическая точка привязки (и вы не можете привязываться к прототипу ячейки). Итак, вот что я сделал:

  1. Во-первых, создайте скрытую кнопку UIButton размером 1px X 1px в представлении контроллеров представления. (важно указать ограничения кнопки, которые позволят перемещать ее в любом месте представления)
  2. Затем сделайте выход для кнопки (я назвал ее popOverAnchorButton) в вашем контроллере представления и перетащите элемент управления от скрытой кнопки к контроллеру представления, к которому вы хотите перейти. Сделайте это переходом popOver.

Теперь у вас есть всплывающее окно с «легальной» привязкой. Кнопка спрятана, так что никто не сможет ее случайно коснуться. Вы используете это только для точки привязки.

Теперь просто вызовите свой переход вручную в своей функции, как это.

    - (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];
    }

И...... Вуаля. Теперь вы используете магию переходов во всем их величии, и у вас есть динамическая опорная точка, которая, кажется, указывает на вашу ячейку. теперь в -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender вы можете просто привести отправителя к классу вашей ячейки (при условии, что вы выполняете надлежащие проверки типа отправителя и какой переход вызывается) и передать объекту ячейки targetViewController перехода.

Дайте мне знать, если это поможет, или у кого-нибудь есть какие-либо отзывы или улучшения.

person johnrechd    schedule 25.01.2013
comment
Кроме того, если это поможет вам. Если бы вы могли проголосовать за это. Я новый пользователь, и мне нужно заработать кредит ботаника, чтобы делать что-либо. - person johnrechd; 25.01.2013
comment
Знаете ли вы, что Xcode 5/iOS 7 улучшили что-то в этой области? В любом случае отличный обходной путь. - person neural5torm; 16.09.2013
comment
Я еще не уверен, хотя я так не думаю. Я обновляю приложение, которое использует это на следующей неделе для iOS7, и при необходимости обновлю. - person johnrechd; 22.09.2013
comment
У меня была такая же проблема в Xcode 5/iOS 7... Жаль, что это действительно необходимо, но это помогло мне. Спасибо. - person Jasper Kuperus; 14.12.2013
comment
Я согласен, что это позор, спасибо за обновление для Xcode 5/iOS 7 - person johnrechd; 17.12.2013
comment
Использование скрытой кнопки — отличная идея. Я использую его, чтобы показать всплывающее окно, указывающее на определенный div внутри webView. Вместо использования frames я использую ограничения и автомакет. Его еще проще реализовать. - person Mike Keskinov; 01.05.2014
comment
Какой код нам нужно написать в -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender? Непонятно из приведенного выше объяснения.. помогите, пожалуйста.. мое всплывающее окно просто указывает далеко от упомянутого кадра.. в чем может быть проблема!! - person Vjlakshmi; 11.06.2014
comment
@Vjlakshmi, вот сообщение о переполнении стека, которое может дать вам хорошее объяснение передачи данных с переходами. stackoverflow.com/questions/20017026 / - person johnrechd; 13.06.2014
comment
Это не сработало для меня, и моя проблема заключалась в том, что изменение рамки не перемещало кнопку. Я изменил код, чтобы изменить центр кнопки, и это сработало. CGPoint tapPoint = CGPointMake(cell.frame.origin.x + cell.frame.size.width, cell.center.y + self.tableView.frame.origin.y - self.tableView.contentOffset.y); self.hiddenAnchorButton.center = tapPoint; - person Kurt Anderson; 23.03.2015
comment
@KurtAnderson, извините, что перемещение кадра не сработало для вас. Я предполагаю, что, возможно, вы использовали фиксированные ограничения и, возможно, не могли их сломать. Я не пробовал это, чтобы увидеть, будет ли это проблемой, но я думаю, что это так. У меня есть только ограничения › ‹ для моей скрытой кнопки. Правильный способ сделать это, вероятно, состоял бы в том, чтобы получить доступ к ограничениям и настроить их в коде, а не во фрейме. Если у меня будет возможность, я протестирую и обновлю. Спасибо. - person johnrechd; 23.03.2015
comment
@johnrechd, нет никаких ограничений на кнопку. Настройка центра сработала, и я получаю именно то, что мне нужно. Это только для приложения iOS 8. - person Kurt Anderson; 24.03.2015
comment
@КуртАндерсон, интересно. Хотя в автомакете, не накладывая ограничений на кнопку, вы позволяете системе накладывать ограничения во время выполнения, что, вероятно, по-прежнему является ограничивающим фактором. Я проверю этот сценарий. - person johnrechd; 24.03.2015
comment
Используя это в разделенном представлении, я обнаружил, что:CGPoint popoverCentre = CGPointMake(cell.center.x, cell.center.y + self.tableView.contentOffset.y); [self.popoverAnchorButton setCenter:popoverCentre]; работает хорошо. - person matt_s; 17.10.2015

Просто добавьте этот ответ в качестве альтернативного способа представить всплывающее окно из затронутой ячейки, хотя он использует код, а не переход. Это довольно просто, и у меня работало от 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];
}
person James Toomey    schedule 30.06.2014

Быстрый ответ с использованием popoverPresentationController: используя раскадровку, настройте новый контроллер представления с идентификатором раскадровки 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

}
person Drewster    schedule 03.08.2015
comment
Это работает для меня внутри prepareForSegue и для всплывающего перехода. Мне не нужно конвертировать прямоугольник или делать какие-либо другие старые обходные пути с представлением 1x1 pt. Я также установил для sourceView в раскадровке значение tableView. Это помогает адаптироваться к раздельному представлению, поскольку всплывающее окно в основном скользило за пределы экрана, когда основное представление было скрыто в настройках многозадачности. - person James; 23.10.2015

Я сделал это самым простым способом:

  1. Сделайте этот Popover Presentation Segue в раскадровке, как обычно, но перетащите из ViewController (не кнопка)
  2. выбрать вид привязки в виде таблицы
  3. затем в ячейке табличного представления коснитесь кнопки make:

     private func presentCleaningDateDatePicker(from button: UIButton) {
        performSegue(withIdentifier: "Date Picker Popover Segue", sender: button)
    }
    
  4. и реализовать метод подготовки (для перехода)

      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
            }
        }
    }
    
person Michał Ziobro    schedule 18.05.2018