How to correctly present a popover from a UITableViewCell with UIPopoverArrowDirectionRight or UIPopoverArrowDirectionLeft
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 (port开发者_运维技巧rait 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.
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.
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
}
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
}
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;
}
I did come across this problem as well. A solution for me was to simply change the width of the rect returned by CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath
:
CGRect rect = [aTableView rectForRowAtIndexPath:indexPath];
//create a 10 pixel width rect at the center of the cell
rect.origin.x = (rect.size.width - 10.0) / 2.0;
rect.size.width = 10.0;
[self.addExpensePopoverController presentPopoverFromRect:rect inView:aTableView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
This create a rect centred inside the cell. This way, the popover has more chances of findind a good spot to position itself.
I had the same kind of problem, here's the workaround i used :
- in my UITableViewCell, i added Actions (IBActions as i generate my cells from a NIB) for cell's specific buttons.
- i then defined a CellActionDelegate protocol that mimics my actions selectors, to which i had my button (sender) and my cell (self)
- then the detailViewController of my splitViewController implements this protocol, converting from the cell's to its coordinates...
here a example of code
In MyCustomTableViewCell.m :
-(IBAction)displaySomeCellRelativePopover:(id)sender{
//passes the actions to its delegate
UIButton *button = (UIButton *)sender;
[cellActionDelegate displaySomeCellRelativePopoverWithInformation:self.info
fromButton:button
fromCell:self];
}
and the, in MyDetailViewController.m :
-(void)displaySomeCellRelativePopoverWithInformation:(MyCellInformationClass *)info
fromButton:(UIButton *)button
fromCell:(UIView *)cell{
UIPopoverController * popoverController = nil;
//create your own UIPopoverController the way you want
//Convert your button/view frame
CGRect buttonFrameInDetailView = [self.view convertRect:button.frame fromView:cell];
//present the popoverController
[popoverController presentPopoverFromRect:buttonFrameInDetailView
inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];]
//release objects created...
}
PS : Of course, the 'action' doesn't have to be a IBAction, and the frame from where the popover originates doesn't have to be a UIButton - a single UIView would be good :)
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];
}
The cell frame is going to be something like 0,0,width,size, i dont believe it will have its X and Y relative to the tableView...you want to use - (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath for this, this should return you the correct frame for the cell relative to the tableView...here is a link UITAbleView ref
The accepted answer cannot compile now.
CGRect rect =[tableView rectForRowAtIndexPath:indexPath]; //This is how to get the correct position on the tableView
UIPopoverPresentationController *popController = [controller popoverPresentationController];
popController.sourceRect = rect;
popController.permittedArrowDirections = 0; //Here there is no arrow. It is my app's need
popController.delegate = self;
popController.sourceView = self.tableView;
popController.sourceView.backgroundColor = [UIColor yellowColor];
This is how i did and works perfectly fine.
RidersVC *vc = [RidersVC ridersVC];
vc.modalPresentationStyle = UIModalPresentationPopover;
vc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
UIPopoverPresentationController *popPresenter = [vc popoverPresentationController];
popPresenter.sourceView = vc.view;
popPresenter.barButtonItem= [[UIBarButtonItem alloc] initWithCustomView:button];
popPresenter.backgroundColor = [UIColor colorWithRed:220.0f/255.0f green:227.0f/255.0f blue:237.0f/255.0f alpha:1.0];
[self.parentVC presentViewController:vc animated:YES completion:NULL];
This also worked for me:
//Which are the ABSOLUTE coordinates in from the current selected cell CGRect frame =[self.view convertRect:[tbvEventsMatch rectForRowAtIndexPath:indexPath] fromView:tbvEventsMatch.viewForBaselineLayout];
精彩评论