How to correctly present a popover from a UITableViewCell with UIPopoverArrowDirectionRight or UIPopoverArrowDirectionLeft
Solution 1
Here is the simple solution which works fine for me
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
CGRect rect=CGRectMake(cell.bounds.origin.x+600, cell.bounds.origin.y+10, 50, 30);
[popOverController presentPopoverFromRect:rect inView:cell permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
Solution 2
You're getting the frame from the cell via the rectForRowAtIndexPath method. This is correct. However the tableview is most likely a subview of a larger iPad view so when the popover gets the coordinates it thinks they're in the larger view. This is why the popover appears in the wrong place.
Example, the CGRect for the row is (0,40,320,44). Instead of the popover targeting that frame on the tableview it instead targets that frame on your main view.
I solved this problem by converting the frame from the relative coordinates of the table to coordinates in my larger view.
code:
CGRect aFrame = [self.myDetailViewController.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:theRow inSection:1]];
[popoverController presentPopoverFromRect:[self.myDetailViewController.tableView convertRect:aFrame toView:self.view] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
Hope that helps others searching for this issue.
Solution 3
In Swift, between the above answers this works for me on an iPad in any orientation:
if let popOverPresentationController : UIPopoverPresentationController = myAlertController.popoverPresentationController {
let cellRect = tableView.rectForRowAtIndexPath(indexPath)
popOverPresentationController.sourceView = tableView
popOverPresentationController.sourceRect = cellRect
popOverPresentationController.permittedArrowDirections = UIPopoverArrowDirection.Any
}
Solution 4
I came across this problem today, and I've found a simpler solution.
When instantiating the popover, you need specify the cell's content view:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *aViewController = [[UIViewController alloc] init];
// initialize view here
UIPopoverController *popoverController = [[UIPopoverController alloc]
initWithContentViewController:aViewController];
popoverController.popoverContentSize = CGSizeMake(320, 416);
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[popoverController presentPopoverFromRect:cell.bounds inView:cell.contentView
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[aView release];
// release popover in 'popoverControllerDidDismissPopover:' method
}
Solution 5
To pop a popover next to the accesory u can use this code :)
I use this for more advanced use:
- finds custom accesoryView (cell.accesoryView)
- if empty, find generated accesoryView (UIButton) if cell has
- if the UIButton doesn't exists, find cell contet view (UITableViewCellContentView)
- if the cell contet view doesn't exists, use cell view
Can be use for UIActionSheet or UIPopoverController.
Here is my code:
UIView *accessoryView = cell.accessoryView; // finds custom accesoryView (cell.accesoryView)
if (accessoryView == nil) {
UIView *cellContentView = nil;
for (UIView *accView in [cell subviews]) {
if ([accView isKindOfClass:[UIButton class]]) {
accessoryView = accView; // find generated accesoryView (UIButton)
break;
} else if ([accView isKindOfClass:NSClassFromString(@"UITableViewCellContentView")]) {
// find generated UITableViewCellContentView
cellContentView = accView;
}
}
// if the UIButton doesn't exists, find cell contet view (UITableViewCellContentView)
if (accessoryView == nil) {
accessoryView = cellContentView;
}
// if the cell contet view doesn't exists, use cell view
if (accessoryView == nil) {
accessoryView = cell;
}
}
[actionSheet showFromRect:accessoryView.bounds inView:accessoryView animated:YES];
Tested in iOS 4.3 to 5.1
Best to use as custom method:
-(UIView*)getViewForSheetAndPopUp:(UITableViewCell*)cell;
And method code:
-(UIView*)getViewForSheetAndPopUp:(UITableViewCell*)cell {
UIView *accessoryView = cell.accessoryView;
if (accessoryView == nil) {
UIView *cellContentView = nil;
for (UIView *accView in [cell subviews]) {
if ([accView isKindOfClass:[UIButton class]]) {
accessoryView = accView;
break;
} else if ([accView isKindOfClass:NSClassFromString(@"UITableViewCellContentView")]) {
cellContentView = accView;
}
}
if (accessoryView == nil) {
accessoryView = cellContentView;
}
if (accessoryView == nil) {
accessoryView = cell;
}
}
return accessoryView;
}
![Omer](https://i.stack.imgur.com/paTQw.jpg?s=256&g=1)
Omer
Ancient History: Graphic + 3D Designer and post-producer. Recent History: App Developer, mobile focused, UI/UX oriented. Present: Anti-Hero @OrangeLoops, watching Kezmo grow from the ground up. What's out there? Here: Kezmo CloseNet Some open source: OLSDynamicHeaderViewController
Updated on September 15, 2020Comments
-
Omer almost 4 years
I always try to present a popover from a cell inside a tableView this way:
[myPopover presentPopoverFromRect:cell.frame inView:self.tableView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
but I cannot use UIPopoverArrowDirectionRight or Left, because, depending on the position of the ipad (portrait or landscape), the popover appears someplace else.
Am I presenting the popover the right way?
PS: the table view is in the detailView of a splitView.
-
Omer about 14 yearsHey! thanks for the answer.. but with the method you recommend I get the same CGrect, and I still have the problem with the popover's position. When the ipad is in portrait mode, the popover appears in the top left corner of the screen when I specify those arrow directions... it freaks me out!!! .. by the other hand,is it correct to present it from self.TableView? Thank you anyway
-
Omer almost 14 yearsHey.. thanks, I'll give it a try.. this seems a smart workaround jeje
-
Omer about 13 yearsJust a question, Can you instantiate an UIPopoverController using that method with only a UIVew? I'll; give it a try =D
-
antalkerekes about 13 years@Omer Not sure what you mean by this. Can you clarify?
-
Peter Johnson almost 13 yearsThis fixed my problem of it working in portrait, but not in landscape. Thanks
-
casey over 12 yearsI used this method to attach an ActionSheet to the detailText of a tableviewcell on an iPad.
CGRect rectToUse = targetCell.bounds; rectToUse.origin.x = rectToUse.size.width - 200; rectToUse.size.width -= rectToUse.origin.x;
-
Clafou almost 12 yearsThis is the right answer. The documentation for presentPopoverFromRect says the rect is the rectangle in view at which to anchor the popover window and the view is the view containing the anchor rectangle for the popover, so it's actually simpler than it seems!
-
lagos almost 12 yearsInitiate a UIPopovercontroller with UIView will give you a warning and a NSInvalidArgumentException when you try to execute. You should put it in a UIViewController, and then initiate the popovercontroller.
-
jt_uk about 11 yearsThis would indicate that the origin of the presentation rect is 600 to the right of the cell's bounds origin (and 10 down from the top of the cell's origin). You could adjust these to place the presentation from anywhere within your cell.
-
James Perih almost 11 yearsTested working in iOS6SDK. I didn't try the custom method, just in the ViewController's-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
-
Enrico Susatyo almost 11 yearsMost people shouldn't do this. Never use hardcoded numbers like this, and this wouldn't work when the orientation changed.
-
user1244109 almost 9 yearsThis is definitely helpful. I was getting the frame from the cell itself,
[tableView cellForRowAtIndexPath:indexPath]
, which is a bad thing to do. Wasn't even aware of-rectForRowAtIndexPath:
. Thanks! -
Joony almost 9 yearsThis is nice if you want to setup the segue in a Storyboard. You can just set the sourceRect property from the prepareForSegue: method.
-
Saif over 8 yearsWe should not hard code the x value of rect, as we have devices with different resolutions
-
rahul over 7 yearsGreat answer :D
-
cvb over 7 yearsswift 3: [self.tableView .rectForRow(at: indexPath)];