I want to have a persistent button in the bottom right corner of my app. During all view transitions, the button should remain static. I'm having trouble deciding what view to add the button to. I know the button ought to be stored in the AppDelegate, but I don't know what other view it would be sense to add it to except the window. One downside of adding it to the window is that when there's an app running in the background (ie Phone), the added status bar padding will push down the window. In general, adding it to the window seems to be a hacky solution -- any thoughts?
Yes, adding it to the UIWindow would be extremely hacky and finicky.
Storyboards
If you're using Storyboards and iOS 5.0 onwards, you should be able to use container views and do something like this:
Here's another picture showing the, rather simplistic, structure of the first View Controller:
The view controller on the left has a container, and then a view which holds the button on top of it. The container indicates that the navigation controller (directly to the right) should appear within itself, that relationship is shown by the =([])=> arrow (formally known as an embed segue). Finally the navigation controller defines its root view controller to the one on the right.
In summary, the first view controller pancakes-in the container view with the button on top, so everything that happens inside has to have the button on top.
Using childViewControllers
aka. The "I hate Storyboards and puppies" mode
Using a similar structure to the Storyboard version, you could create the base view controller with its button, and then, add the view that will become then new "root" of the application, underneath.
To make it clear, let's call the one view controller that holds the button the FakeRootViewController, and the view controller that will be, for all practical purposes, the root of the application: RootViewController. All subsequent view controllers won't even know that there's the FakeRootViewController above everyone else.
FakeRootViewController.m
// The "real" root
#import "RootViewController.h"
// Call once after the view has been set up (either through nib or coded).
- (void)setupRootViewController
{
// Instantiate what will become the new root
RootViewController *root = [[RootViewController alloc] <#initWith...#>];
// Create the Navigation Controller
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:root];
// Add its view beneath all ours (including the button we made)
[self addChildViewController:nav];
[self.view insertSubview:nav.view atIndex:0];
[nav didMoveToParentViewController:self];
}
AppDelegate.m
#import "FakeRootViewController.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
FakeRootViewController *fakeRoot = [[FakeRootViewController alloc] <#initWith...#>];
self.window.rootViewController = fakeRoot;
[self.window makeKeyAndVisible];
return YES;
}
That way, you can have all the benefits of inserting the button on the window, without all the guilt and "Should I really be a programmer?" that it causes.
Potentially you could have 1 main "root" view controller, and all you other view controllers could be child view controllers, with their views as child views. Then they would have their content, and the button would be in the "root" view controller. But this seems just as sketchy and hacky as putting it in the window, and probably less convenient.
I use this button:
#interface UIPopUpButton : UIImageView <UIPopoverControllerDelegate, UIActionSheetDelegate>
{
UIPopoverController* popoverController;
Class popoverClass;
}
- (id) initWithPoint: (CGPoint) point;
- (void) touchesBegan: (NSSet*) touches
withEvent: (UIEvent*) event;
+ (id) buttonAtPoint: (CGPoint) point;
+ (id) buttonAtOriginalPoint;
+ (void) unhighlight;
+ (void) bringButtonToFront;
#property (nonatomic, retain) UIPopoverController* popoverController;
#property (nonatomic, assign) Class popoverClass;
#end
#import "UIPopUpButton.h"
#implementation UIPopUpButton
static UIPopUpButton* button = nil;
static CGPoint originalPoint;
#synthesize popoverClass;
#synthesize popoverController;
+ (id) buttonAtPoint: (CGPoint) point
{
if (button == nil)
{
button = [[UIPopUpButton alloc] initWithPoint: point];
originalPoint = point;
button.popoverClass = [UIPopoverController class];
}
else
{
button.frame = CGRectMake(point.x, point.y, button.frame.size.width, button.frame.size.height);
}
return button;
}
+ (id) buttonAtOriginalPoint
{
return [self buttonAtPoint: originalPoint];
}
+ (void) unhighlight
{
button.highlighted = NO;
}
+ (void) bringButtonToFront
{
[[UIApplication sharedApplication].keyWindow addSubview: [self buttonAtOriginalPoint]];
}
- (id) initWithPoint: (CGPoint) point
{
UIImage* image1 = [UIImage imageNamed: #"topbutton.png"];
UIImage* image2 = [UIImage imageNamed: #"topbutton.png"];
if ((self = [super initWithImage: image1
highlightedImage: image2]))
{
self.userInteractionEnabled = YES;
self.frame = CGRectMake(point.x, point.y, self.frame.size.width, self.frame.size.height);
self.multipleTouchEnabled = NO;
}
return self;
}
- (BOOL) isAppCurrStatus
{
return ([DevToolsClientController sharedInstance].statusOfRootViewController == FrontEndApplication);
}
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
if(touch.view == self)
{
if (self.popoverController == nil)
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle: #"Please choice operation:"
delegate: self
cancelButtonTitle: nil
destructiveButtonTitle: nil
otherButtonTitles: nil];
[actionSheet addButtonWithTitle: #"Cancel"];
actionSheet.cancelButtonIndex = 0;
[actionSheet addButtonWithTitle: #"Button 1"];
actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
[actionSheet setTag: 0];
[actionSheet setDelegate: self];
[actionSheet showInView: [self superview]];
[actionSheet release];
[actions release];
}
else
{
PopoverMenuController* contentViewController = [[PopoverMenuController alloc] init];
self.popoverController = [[UIPopoverController alloc] initWithContentViewController: contentViewController];
popoverController.delegate = self;
[popoverController presentPopoverFromRect: CGRectMake(10.0f, 10.0f, 5.0f, 5.0f)
inView: self
permittedArrowDirections: UIPopoverArrowDirectionAny
animated: YES];
contentViewController.popoverController = self.popoverController;
[contentViewController reloadData];
}
}
else
{
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
}
}
[super touchesBegan: touches withEvent: event];
}
#pragma mark UIActionSheetDelegate implementation
-(void) actionSheet: (UIActionSheet*) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex
{
NSNumber* indexAction = [[NSNumber alloc] initWithInt: buttonIndex - 1];
}
- (void) runAction: (NSNumber*) indexAction
{
[DevToolsPopoverMenuController runAction: [indexAction integerValue]];
}
#pragma mark -
#pragma mark UIPopoverControllerDelegate implementation
- (void) popoverControllerDidDismissPopover: (UIPopoverController*) thePopoverController
{
if (self.popoverController != nil)
{
self.popoverController = nil;
}
}
- (BOOL) popoverControllerShouldDismissPopover: (UIPopoverController*) thePopoverController
{
//The popover is automatically dismissed if you click outside it, unless you return NO here
return YES;
}
#end
call:
[UIPopUpButton bringButtonToFront];
My button is always on top.
Try subclassing the UIViewController class and make your own one with the button
Create a singleton object that holds the button so all view controllers can reference it and add it to their subview or add it to the window directly.
SomeClass.h
#property (nonatomic) UIButton *yourButton;
+(SomeClass*)sharedSomeClass;
SomeClass.m
#synthesize yourButton = _yourButton;
-(id)init
{
self = [super init];
if(self)
{
_yourButton = [UIButton new];
//Other settings you want for your button
}
return self;
}
+(SomeClass)sharedSomeClass
{
static SomeClass *sharedSomeClass;
if (!sharedSomeClass)
sharedSomeClass = [[super allocWithZone:nil]init];
return sharedSomeClass;
}
+(void)allocWithZone:(NSZone*)zone
{
return [self sharedSomeClass];
}
If you like you can access the window directly like this:
UIWindow *mainwindow = [[[UIApplication sharedApplication]delegate]window];
import SomeClass.h into your view controllers, and access the button from anywhere
#import "SomeClass.h"
SomeClass *someClass = [SomeClass sharedSomeclass];
UIButton *localButton = someClass.yourButton;
Related
I am trying to achieve a modal presentation effect where the presented view covers the parent view only partially as shown in the picture below.
I know I could achieve this by implementing custom transitions using UIPresentationController. I don't want to reinvent the wheel so before I roll on with development I would like to ask.
Is there a build in support for this kind of transition in the APIs?
I researched all available Modal Presentation Styles and it appears to me there is no support for the transition I want to make and the only way of achieving it is just to code it.
I ran into this exact same issue. I went down the modal presentation styles route as well and kept hitting a wall (specifically getting it working on an iPhone rather than an iPad).
After some digging around, I was able to get it working though. Here's how I did it:
To start, we need a view controller that we will be presenting (the modal one) to set it's view's background color to transparent and set the frame of the navigation controller's view to some offset.
ModalViewController.h
#import UIKit;
#class ModalViewController;
#protocol ModalViewControllerDelegate <NSObject>
- (void)modalViewControllerDidCancel:(ModalViewController *)modalViewController;
#end
#interface ModalViewController : UIViewController
#property (weak, nonatomic) id<ModalViewControllerDelegate> delegate;
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
#end
ModalViewController.m
static const CGFloat kTopOffset = 50.0f;
#implementation ModalViewController {
UINavigationController *_navController;
}
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
rootViewController.navigationItem.leftBarButtonItem = [self cancelButton];
_navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
self.view.backgroundColor = [UIColor clearColor];
[self.view addSubview:_navController.view];
// this is important (prevents black overlay)
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect bounds = self.view.bounds;
_navController.view.frame = CGRectMake(0, kTopOffset, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - kTopOffset);
}
- (UIBarButtonItem *)cancelButton
{
return [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(cancelButtonClicked:)];
}
- (void)cancelButtonClicked:(id)sender
{
[_delegate modalViewControllerDidCancel:self];
}
#end
Next, we need to set up the presenting controller to run the following animation:
Scale itself down
Fade out a lil' bit
Present the modal view controller using presentViewController:animated:completion
This is what I did
PresentingViewController.m
static const CGFloat kTransitionScale = 0.9f;
static const CGFloat kTransitionAlpha = 0.6f;
static const NSTimeInterval kTransitionDuration = 0.5;
#interface PresentingViewController <ModalViewControllerDelegate>
#end
#implementation PresentingViewController
...
...
- (void)showModalViewController
{
self.navigationController.view.layer.shouldRasterize = YES;
self.navigationController.view.layer.rasterizationScale = [UIScreen mainScreen].scale;
UIViewController *controller = // init some view controller
ModalViewController *container = [[ModalViewController alloc] initWithRootViewController:controller];
container.delegate = self;
__weak UIViewController *weakSelf = self;
[UIView animateWithDuration:kTransitionDuration animations:^{
weakSelf.navigationController.view.transform = CGAffineTransformMakeScale(kTransitionScale, kTransitionScale);
weakSelf.navigationController.view.alpha = kTransitionAlpha;
[weakSelf presentViewController:container animated:YES completion:nil];
} completion:^(BOOL finished) {
weakSelf.navigationController.view.layer.shouldRasterize = NO;
}];
}
#pragma mark - ModalViewControllerDelegate
- (void)modalViewControllerDidCancel:(ModalViewController *)modalViewController
{
__weak UIViewController *weakSelf = self;
[UIView animateWithDuration:kTransitionDuration animations:^{
weakSelf.navigationController.view.alpha = 1;
weakSelf.navigationController.view.transform = CGAffineTransformIdentity;
[weakSelf dismissViewControllerAnimated:YES completion:nil];
}];
}
#end
pretty sure its done like this
let newVC = <view controller you want to display>
let nav: UINavigationController = UINavigationController(rootViewController: newVC)
if let currVc = UIApplication.sharedApplication().keyWindow?.rootViewController {
nav.transitioningDelegate = currVc
nav.modalPresentationStyle = UIModalPresentationStyle.Custom;
currVc.presentViewController(nav, animated: true, completion: nil)
}
I'm pretty sure this is your answer - Page sheet - as in UIModalPresentationPageSheet
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/mobilehig/Alerts.html#//apple_ref/doc/uid/TP40006556-CH14-SW3
I am new to Objective-C and Xcode and I am having difficulty in changing views. I understand you can use the UIScrollView to swipe between image views, but I would like to simply swipe right to change views, and swipe left to return. I have AppDelegate.h and AppDelegate.m which have not been changed. ViewController.h and ViewController.m are given below:
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
ViewController.m:
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//sets up gesture recognizer
UISwipeGestureRecognizer *swipeRightGesture=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
[self.view addGestureRecognizer:swipeRightGesture];
swipeRightGesture.direction=UISwipeGestureRecognizerDirectionRight;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)handleSwipeGesture:(UISwipeGestureRecognizer *)sender
{
NSUInteger touches = sender.numberOfTouches;
if (touches == 2)
{
if (sender.state == UIGestureRecognizerStateEnded)
{
ViewController2 *vc2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[self.navigationController pushViewController:vc2 animated:YES];
}
}
}
#end
I also have ViewController2.h and ViewController2.m which I have linked to the second view in storyboard along with a gesture that I have linked to the handleSwipeGesture method above.
- (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *oneFingerSwipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(oneFingerSwipeLeft:)] ;
[oneFingerSwipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[[self view] addGestureRecognizer:oneFingerSwipeLeft];
}
- (void)oneFingerSwipeLeft:(UITapGestureRecognizer *)recognizer
{
NSLog(#"Swiped left");
// Add your navigation Code Here
}
U can use this if u want to use single finger swipe. IF u want to add two finger Swipe add your
NSUInteger touches = sender.numberOfTouches;
if (touches == 2)
{
if (sender.state == UIGestureRecognizerStateEnded)
{
}
}
above code in the (void)onefingerSwipeLeft method
- (IBAction)handleSwipeGesture:(UISwipeGestureRecognizer *)sender does not need to be an IBAction since it is not hooked up to an IBOutlet. It should return void instead.
Additionally, instead of checking the number of touches once the gesture has already been recognized, you can make two touches a requirement for it to be recognized at all, by using the line:
swipeRightGesture.numberOfTouchesRequired = 2;
Consequently, the method -(void)handleSwipeGesture no longer needs to take an argument, so you can remove the colon from the line where you declare the gesture recognizer.
The final code might look similar to this:
UISwipeGestureRecognizer *swipeRightGesture=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture)];
swipeRightGesture.direction=UISwipeGestureRecognizerDirectionRight;
swipeRightGesture.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:swipeRightGesture];
...
...
- (void)handleSwipeGesture {
ViewController2 *vc2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[self.navigationController pushViewController:vc2 animated:YES];
}
I'm trying to make a view fall as a custom segue transition, but when the perform method is called in the UIStoryboardSegue implementation, it does not fall. I have tried moving the view to be dropped into the source's view to see if it does anything, but it doesn't.
-(void)perform {
UIViewController *src = (UIViewController *)self.sourceViewController;
UIViewController *dest = (UIViewController *)self.destinationViewController;
UIView *viewdrop = [dest.view snapshotViewAfterScreenUpdates:YES];
viewdrop.frame = CGRectMake(0, -src.view.frame.size.height, dest.view.frame.size.width, dest.view.frame.size.height);
[src.view addSubview:viewdrop];
animator = [[UIDynamicAnimator alloc] initWithReferenceView:src.view];
UIGravityBehavior* gravityBehavior = [[UIGravityBehavior alloc] initWithItems:#[viewdrop]];
[animator addBehavior:gravityBehavior];
}
The reason it doesn't drop is because the gravity behavior takes time, but the segue itself is deallocated as soon as the perform method finishes. So, you need a way to keep the segue alive at least until the movement is complete. One way to do this is to make a strong property for the segue in the source view controller, and set its value in prepareForSegue,
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
self.dropSegue = segue;
}
I made a modified version of your segue that also adds a collision behavior, and sets the source view controller as the delegate of the collision behavior, so I can use the delegate method, collisionBehavior:endedContactForItem:withBoundaryIdentifier:, to set the dropSegue property to nil (after a slight delay) which causes the segue to be deallocated,
-(void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier {
NSLog(#"collision ended with %#", identifier);
[self performSelector:#selector(setDropSegue:) withObject:nil afterDelay:1];
}
Here is my version of the gravity drop segue,
#interface RDSegue ()
#property (strong, nonatomic) UIDynamicAnimator *animator;
#end
#implementation RDSegue
-(id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination {
if (self = [super initWithIdentifier:identifier source:source destination:destination]) {
UIViewController *src = self.sourceViewController;
UIViewController *dest = self.destinationViewController;
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:src.view];
[src addChildViewController:dest];
[dest didMoveToParentViewController:src];
dest.view.frame = CGRectMake(0, -src.view.bounds.size.height, src.view.bounds.size.width, src.view.bounds.size.height);
[src.view addSubview:dest.view];
}
return self;
}
-(void)perform {
UIGravityBehavior* gravityBehavior = [[UIGravityBehavior alloc] initWithItems:#[[self.destinationViewController view]]];
UICollisionBehavior *collide = [[UICollisionBehavior alloc] initWithItems:#[[self.destinationViewController view]]];
CGPoint left = CGPointMake(self.animator.referenceView.bounds.origin.x, self.animator.referenceView.bounds.origin.y + self.animator.referenceView.bounds.size.height);
CGPoint right = CGPointMake(self.animator.referenceView.bounds.origin.x + self.animator.referenceView.bounds.size.width, self.animator.referenceView.bounds.origin.y + self.animator.referenceView.bounds.size.height);
[collide addBoundaryWithIdentifier:#"bottom" fromPoint:left toPoint:right];
[collide setCollisionDelegate:self.sourceViewController];
[self.animator addBehavior:gravityBehavior];
[self.animator addBehavior:collide];
}
-(void)dealloc {
NSLog(#"In dealloc");
}
I use UIPopoverController to popup an view in iPad iOS7 like this:
if (!self.popover) {
UIViewController *popupVC = [[UIViewController alloc] init];
[popupVC.view addSubview:thePopupView];
popupVC.preferredContentSize = CGSizeMake(240, 140);
self.popover = [[UIPopoverController alloc] initWithContentViewController:popupVC];
self.popover.delegate = self;
}
[self.popover presentPopoverFromBarButtonItem:barButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
But when popover active, it make screen darker while this effect not affect other views in iOS6.
How to overcome this issue? Thanks!
If you mean the dimming view that is inserted under the popover, there is only one workaround - use a custom popoverBackgroundViewClass.
It's complicated, but not as complicated as you might think.
Another method is to traverse the popover view stack and remove the dimming view manually, as shown here in a UIPopoverController subclass:
#property (nonatomic, assign) BOOL showsDimmingView;
....
- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated
{
[super presentPopoverFromBarButtonItem:item
permittedArrowDirections:arrowDirections
animated:animated];
if (!_showsDimmingView) {
[self removeDimmingView:[[UIApplication sharedApplication].keyWindow.subviews lastObject]];
}
}
- (void)presentPopoverFromRect:(CGRect)rect
inView:(UIView *)view
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated
{
[super presentPopoverFromRect:rect
inView:view
permittedArrowDirections:arrowDirections
animated:animated];
if (!_showsDimmingView) {
[self removeDimmingView:[[UIApplication sharedApplication].keyWindow.subviews lastObject]];
}
}
- (void)removeDimmingView:(UIView *)subview
{
for (UIView *sv in subview.subviews) {
if (sv.alpha == 0.15f && [sv isKindOfClass:NSClassFromString(#"_UIPopoverViewBackgroundComponentView")]) {
sv.alpha = 0.f;
}
const CGFloat *components = CGColorGetComponents(sv.backgroundColor.CGColor);
if (sv.backgroundColor && (components[1] == 0.15f || sv.alpha == 0.15f)) {
[sv removeFromSuperview];
}
[self removeDimmingView:sv];
}
}
I've implemented a custom story board segue which is working fine under iOS 6 but nothing happens in the simulator under iOS 5.1.
The problem is that the popoverController is always nil under iOS 5.1?!
#implementation PopoverFromRectSegue
-(id)initWithIdentifier:(NSString *)identifier
source:(UIViewController *)source
destination:(UIViewController *)destination {
if(self = [super initWithIdentifier:identifier
source:source
destination:destination]) {
}
return self;
}
- (void)perform {
UIPopoverController *popCtrl = ((UIStoryboardPopoverSegue *)self).popoverController;
id controller = [self sourceViewController];
if ([controller isKindOfClass:[UIViewController class]] && [controller respondsToSelector:#selector(popoverRect)]) {
[popCtrl presentPopoverFromRect:[[controller performSelector:#selector(popoverRect)] CGRectValue] inView:[controller view] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
#end
Any help/hint is appreciated.
Edit:
Just made new sample project. It seems that under iOS5.1 the popoverController is not set
for custom UIStoryBoardSegues.
What else can I do.
The normal popover requires an anchor but a prototype tableView cells is not accepted (failure during compile) and I couldn't find a way to modify the rect the popover is presented from.
Here's my workaround.
PopoverFromRectSegue.h
#interface PopoverFromRectSegue : UIStoryboardPopoverSegue
#property (strong, nonatomic) UIPopoverController *popoverCtrl;
#end
PopoverRectFromSegue.m
#import "PopoverFromRectSegue.h"
#implementation PopoverFromRectSegue
- (void)perform
{
UIPopoverController *popCtrl = ((UIStoryboardPopoverSegue *)self).popoverController;
// under iOS 5.1 the popoverController iVar is not set
// so we have to use our own one
if (nil == popCtrl) {
popCtrl = self.popoverCtrl;
}
id controller = [self sourceViewController];
if ([controller isKindOfClass:[UIViewController class]] && [controller respondsToSelector:#selector(popoverRect)]) {
[popCtrl presentPopoverFromRect:[[controller performSelector:#selector(popoverRect)] CGRectValue] inView:[controller view] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
#end
In my prepareForSegue method I do the following.
if (isPad) {
self.popoverCtrl = [(UIStoryboardPopoverSegue *)segue popoverController];
if (nil == _popoverCtrl) {
self.popoverCtrl = [[UIPopoverController alloc] initWithContentViewController:[segue destinationViewController]];
((PopoverFromRectSegue *)segue).popoverCtrl = _popoverCtrl;
}
self.popoverRect = [NSValue valueWithCGRect:[self.myTableView rectForRowAtIndexPath:indexPath]];
}