是否可以手动执行Pop Over Segue(从动态的UITableView单元格)?

30

当用户在动态表视图中触摸单元格时,我需要执行一个 Popover 转场。但是当我尝试使用以下代码时:

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

我尝试手动从动态的 TableView 进行弹出式转场,但是出现了错误:

配置不合法

没有锚点的弹出式转场

有没有什么方法可以实现这个功能?

4个回答

61

今晚我也面临了同样的问题,有几种解决方法(其中包括以老式方式呈现弹出框)。

对于这个示例,我有一个对象存储在我的自定义单元格类中。当选择该单元格时,我调用以下函数打开一个弹出视图控制器,显示关于该对象的详细信息,并将其指向表格中相应的单元格。

    - (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];
    }
这种方法的问题在于我们经常需要popover视图具有自定义初始化器。这是一个问题,如果您想要在storyboard中设计视图而不是xib,并且具有一个自定义init方法,该方法以它的显示为参数使用单元格相关的对象。您也不能只使用popover segue(乍一看)因为您需要动态锚点(并且您无法锚定到单元格原型)。所以我做了以下操作:
  1. 首先,在您的视图控制器视图中创建一个隐藏的1px X 1px UIButton。(重要的是给按钮约束,使其可以在视图中移动到任何位置)
  2. 然后在您的视图控制器中为该按钮创建一个输出口(我称其为popOverAnchorButton),并从隐藏按钮向您希望segue到的视图控制器拖动一个segue。将其设置为弹出式segue。
现在您有一个带有“合法”锚点的popover segue,按钮是隐藏的,所以没有人会不小心触摸它,您只是用它作为锚点。
现在只需在您的函数中手动调用segue即可。
    - (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];
    }

然后...... 瞧!现在你正在使用segue的魔力,拥有动态锚点,它似乎指向你的单元格。 现在在-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender中,您可以将sender简单地转换为您单元格的类(前提是您对sender类型进行适当检查并确定调用哪个segue),并将segue的destinationViewController设置为该单元格的对象。

如果这有帮助,请告诉我,或者任何人有任何反馈或改进意见。


7
如果这能帮到你的话,请点赞支持一下。我是一个新用户,需要通过点赞来积累 nerd credit 才能做任何事情。 - johnrechd
你知道Xcode 5/iOS 7在这个领域有什么改进吗?不管怎样,很棒的解决方法。 - neural5torm
我同意这是一件遗憾的事情,感谢关于Xcode 5/iOS 7的更新。 - johnrechd
1
使用隐藏按钮是个好主意。我在使用它来显示指向webView内特定“div”的弹出窗。我不使用frames,而是采用约束和自动布局,这样更容易实现。 - Mike Keskinov
1
在使用分割视图时发现以下代码工作得很好:CGPoint popoverCentre = CGPointMake(cell.center.x, cell.center.y + self.tableView.contentOffset.y); [self.popoverAnchorButton setCenter:popoverCentre]; - matt_s
显示剩余8条评论

3

我想提供一种用代码而不是segue来呈现弹出视图的替代方法,虽然它与被触摸的单元格有关。这个方法非常简单,并且在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];
}

2
使用popoverPresentationController的快速答案:使用Storyboard,设置新视图控制器,并将其Storyboard ID设置为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

}

这对我来说在prepareForSegue中也适用于弹出窗口转场。我不必转换矩形或执行任何其他1x1 pt视图的旧解决方法。我还在故事板中将sourceView设置为tableView。这有助于在分割视图上进行适应,因为在多任务调整中主要视图被隐藏时我的弹出窗口大部分都滑离了屏幕。 - James

0
我已经用最简单的方式制作了这个:
  1. 在Storyboard中像往常一样将Popover Presentation Segue从ViewController拖出(而不是从按钮)
  2. 选择锚点视图为表格视图
  3. 然后在表格视图单元格的按钮触摸中执行:

     private func presentCleaningDateDatePicker(from button: UIButton) {
        performSegue(withIdentifier: "Date Picker Popover Segue", sender: button)
    }
    
  4. 并实现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
            }
        }
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接