Pointing a popover at a UITableViewRowAction button - ios

I'm trying to launch a popover from one of several UITableViewRowActions in a custom UITableviewCell. It appears that there are no off the shelf delegate methods available to do this in a straightforward way, but my searches turned up the following approaches to the problem:
How to get a reference to button from UITableViewRowAction?
How to correctly start a popover segue from a custom uitableviewcell using the storyboard
How do I implement the UITapGestureRecognizer into my application
Segue from editActionsForRowAtIndexPath
IOS: verify if a point is inside a rect
How to detect a tap gesture in subviews
While each of these was helpful with some aspect of a workaround, none was comprehensive. However, using some guidance from each, I eventually arrived at this code:
else if ([[segue identifier] isEqualToString:#"AccountPopSegue"])
{
UITapGestureRecognizer *tapGestureRecognizer;
CGPoint point = [tapGestureRecognizer locationInView:_thisCustomCell.contentView];
UIView *tappedView = [_thisCustomCell hitTest:point withEvent:nil];
UIViewController *controller = segue.destinationViewController;
controller.popoverPresentationController.delegate = self;
controller.preferredContentSize = CGSizeMake(320, 186);
UIPopoverPresentationController *thisPPC = controller.popoverPresentationController;
thisNavController = (UINavigationController *)segue.destinationViewController;
AccountChangeVC *acCVC = (AccountChangeVC *)thisNavController.topViewController;
acCVC.delegate = self;
thisPPC.sourceRect = tappedView.bounds;
thisPPC.sourceView = tappedView.superview;
}
A tap on the the "Acct" action produces the following behavior, shown for three row cases:
So it appears that I'm close to achieving my desired presentation, but not quite there.
Can anyone suggest changes that will make the popover point directly at the launching UITableViewRowAction button?

Related

IOS Custom Segue and ContainerView

I have three buttons and a UIView (I call it containerView), tap each of the buttons, the containerView will show a View in a UIViewController through a custom segue, the buttons use one IBAction method(switchView), I put the three buttons in a IBOutletCollection called navButtons;
and in viewDidLoad I call switchView to make the containerView show first view controller.
And the code run well, no error,the only question is when I tap first button,the first UIViewController will shown in containerView, the UIImageView (buttons,labels,etc) should be in the centre of the screen, but it never behave like this when it's first loaded, when I tap another button then tap first button again,it behaved as expected,
I have no idea what happened and what's the difference between first load and tapping it again.
IS THERE ANYTHING I MISSED IN THE CODE ?
I am not good at English,Sorry for anything unclear .
- (void)viewDidLoad {
[super viewDidLoad];
self.availableIdentifier = [[NSMutableArray alloc] initWithObjects:#"Seg1",#"Seg2",#"Seg3", nil];
[self switchView:self.navButtons[0]];
}
- (IBAction)switchView:(UIButton *)sender
{
[self setSelectedIndexs:(int)sender.tag];
}
- (void)setSelectedIndexs:(int)index
{
[self performSegueWithIdentifier:self.availableIdentifier[index] sender:self.navButtons[index]];
}
//Code of Custom Segue:
-(void)perform
{
ViewController *controller = (ViewController *)self.sourceViewController;
UIViewController *destController = (UIViewController *)self.destinationViewController;
for (UIView *view in controller.containerView.subviews)
{
[view removeFromSuperview];
}
controller.currentController = destController;
[controller.containerView addSubview:destController.view];
[controller.containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
[destController didMoveToParentViewController:controller];
}
I think you should add:
destController.view.frame = controller.containerView.frame;
before
controller.currentController = destController;
is your clipSubViews set in UIViewController ?
Solved. Thanks for the answer of Bojan Bozovic ;
CGRect frame = controller.containerView.frame;
frame.origin.y = 0;
destController.view.frame = frame;
Add above before controller.currentController = destController;

How to correctly start a popover segue from a custom uitableviewcell using the storyboard

So how do you correctly setup a popover segue using the storyboard when the button opening it is inside a custom prototype cell within a table view?
It should work both on iphone and ipad
It should have the correct position and anchor on ipad
I think the most elegant solution is the following:
Create a segue from the button as usual to the appropriate ViewController
Open the segue and set the anchor to the table view (dragging from the circle to the view).
In prepare for segue add the following:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIViewController *vc = (UIViewController *)segue.destinationViewController;
UIPopoverPresentationController *ppc = vc.popoverPresentationController;
UIButton *button = (UIButton *)sender;
ppc.sourceRect = button.frame;
ppc.sourceView = button.superview;
}

table view slides up under navigation bar when user taps row

I have a UINavigationController (NC) containing a UITableViewController (TVC0). When the user taps a row, it loads a UIPageViewController (PVC), which pages back and forth between other UITableViewControllers (TVC1).
TVC0 shows up inside NC (meaning it doesn't hide behind the navigation bar at the top or the tab bar at the bottom). When it pushes PVC, the first TVC1 appears inside the bounds of the nav bar and tab bar. However when I swipe, the TVC1s inside are hidden behind the navigation bar and tab bar. I can pull to reveal the contents, but when I release, it snaps back to behind the bar.
How can I force everything to appear between the two bars? I can't use storyboard (because it's a legacy app) and the embed in... option isn't available.
[Edit]
I added some logging and discovered that my embedded TVC1s frame has an absolute origin of 0, 64, but as soon as I tap, it goes to 0, 0. If I can't figure out a real solution, I can always fake it by adding 64, but I'd much rather figure out what's actually wrong.
[/Edit]
[More Edit]
I was testing another area in the iOS 6 simulator and discovered that this paging works flawlessly in iOS 6. So the issue I'm seeing is iOS 7 specific.
[/More Edit]
Here is my TVC0 viewDidLoad, PVC pageViewController:viewControllerBeforeViewController:, and a helper viewControllerAtIndex::
- (void) viewDidLoad
{
[super viewDidLoad];
NSDictionary* options = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInteger: UIPageViewControllerSpineLocationMin]
forKey:
UIPageViewControllerOptionSpineLocationKey];
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:
UIPageViewControllerTransitionStyleScroll
navigationOrientation:
UIPageViewControllerNavigationOrientationHorizontal
options: options];
self.pageController.dataSource = self;
self.pageController.view.frame = self.view.frame;
NSArray* viewControllers =
[NSArray arrayWithObject: [self viewControllerAtIndex: self.initialIndex]];
[self.pageController setViewControllers: viewControllers
direction: UIPageViewControllerNavigationDirectionForward
animated: NO
completion: nil];
[self addChildViewController: self.pageController];
[self.view addSubview: self.pageController.view];
[self.pageController didMoveToParentViewController: self];
for (UIGestureRecognizer* recognizer in self.pageController.gestureRecognizers)
{
if ([recognizer isKindOfClass: [UITapGestureRecognizer class]])
{
recognizer.enabled = NO;
}
}
}
// SearchResultsList is TVC1
- (SearchResultsList*) viewControllerAtIndex: (NSUInteger) index
{
if (index >= self.items.count)
{
return nil;
}
SearchResultsList* retVal = [[SearchResultsList alloc]
initWithNibName: #"SearchResultsList" bundle: nil];
MyListItem* myItem = [self.items objectAtIndex: index];
MyMatchesRequest* matches = [[MyMatchesRequest alloc] initWithItemId: myItem.itemId];
[matches execute: ^(MySearchResults* results)
{
retVal.tableData = [NSMutableArray arrayWithArray: results.items];
retVal.view.frame = self.view.frame;
retVal.myItem = myItem;
retVal.index = index;
self.title = myItem.displayText;
[[retVal tableView] reloadData];
}];
return retVal;
}
- (UIViewController*) pageViewController: (UIPageViewController*) pageViewController
viewControllerBeforeViewController: (UIViewController*) viewController
{
SearchResultsList* vc = (SearchResultsList*)viewController;
if (vc.index == 0)
{
[self.navigationController popViewControllerAnimated: YES];
return nil;
}
return [self viewControllerAtIndex: vc.index - 1];
}
I had a very painful learning experience with similar behavior :(
Put this in your view controller's init:
self.automaticallyAdjustsScrollViewInsets = NO;
This is a new UIViewController property that defaults to YES in iOS 7
UIViewController Docs
Because you're presenting view controllers in a container which is less than the full size of the screen you need to set
self.pageViewController.definesPresentationContext = YES;
viewControllerWhichIsApageInPageController.modalPresentationStyle = UIModalPresentationCurrentContext
You code seems a little confused. You say it is your Page View Controller's viewDidLoad, yet it creates a new PVC and adds it as a child view controller to itself as parent. If you are really doing this, you are creating a PVC inside another PVC. I doubt this is what you want.
This code really belongs in TVC0 which invokes the PVC when the user taps a row. This invocation wouldn't be correct in viewDidLoad, but might sit nicely in a didSelectRowAtIndexPath method. Instead of bringing it in as a child controller, we can simply push it onto our navigationController's stack. (I expect you are doing this anyway in your outermost PVC).
But just in case, I would remove these three lines:
[self addChildViewController: self.pageController];
[self.view addSubview: self.pageController.view];
[self.pageController didMoveToParentViewController:self];
and replace them with
[self.navigationController pushViewController:self.pageController
animated:YES];
(this is called from your TVC0 - all of the code you have shown can live in TVC0)
Then to prevent the behaviour you describe, when you create your UINavigationController it should suffice to set the translucent property of it's navigationBar to NO.
update
I have looked at this issue in sufficient detail to see some buggy behaviour as you describe occur in some circumstances, but it's fairly hard to replicate.
The cause of the 'jump-up' behaviour is clear. If you have a translucent navBar, and it's automaticallyAdjustsScrollViewInsets is set to YES, in certain situations this can result in a private subview of the pageViewController (_UIQueuingScrollView) setting it's contentOffset.y to -64 when the pageViewController is first loaded. However as soon as the pageVC gets a chance to update itself (for example by a swipe or other touch gesture) it resets it's internal subviews state, losing that rogue contentOffset. So when you touch the first page, it jumps up. Where it then stays. This may be considered a bug, although it may be the result of misusing the pageViewController.
Just setting automaticallyAdjustsScrollViewInsets to NO doesn't cure your ills, as then your pages are all positioned behind the navBar. What you need to do is adjust the frame of the pageViewController itself.
I could go in to more detail, but at that point it gets hard to apply answers to your specific case as your app design looks a little odd, which is probably contributing to the issue.
if you can set your navigation controllers' navigation bar's translucent property to NO the problem should go away.
as I mentioned earlier, I cannot replicate your issue exactly as you describe, so I think you have not given a full and clear picture of your app design
loading a pageViewController as a child of a tableViewController, and making it's view a subview of the tableView, is a very odd way to go, and is doubtless contributing to your problems.
In viewControllerBeforeViewController the pageViewController child seems to pop it's parentViewController (the tableViewController) - so you would never see it's table contents? Either there is more relevant detail to the app, or you haven't described it accurately.
I recommend you first deal with these issues. Your problem may well then disappear.

iOs - Using container, table view and detailed view - How do I keep detailed view inside container

I'm trying to create a pretty simple app, although I'm stuck on the same issue for days...
I'm using storyboard and my current set up is this:
storyboard
My goal is to keep everything in the "Project View Controller" the same except for what happens inside the container.
The "Comments Table View" shows ok inside my container but when tapping a cell in the tableview, the "Comment Show View" covers the whole screen (note that in storyboard, it inherits the size correctly).
This is how my "prepareForSegue" looks like (I tried fixing the size of the instance to the tableview size):
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"showComment"] ){
CommentShowViewController *csvc = [segue destinationViewController];
[[csvc view] setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
NSIndexPath *path = [commentsTable indexPathForSelectedRow];
Comment *c = [aCommentsList objectAtIndex:[path row]];
[csvc setCurrent:c];
}
}
Any ideas how I can make this work?
Thanks,
Jonathan
I think the problem is the push segue. Push segues needs a UINavigationController to be performed. I guess you have a UINavigationController to the left in the picture that's not shown. When the segue is performed this is the UINavigationController that is used for the segue.
One fix for this issue would be to have another UINavigationController between the ProjectViewController and the CommentsTableView.
Container -> UINavigationController ->CommentsTableView -> CommentShowViewController.
Uncheck "Shows Navigation Bar" in IB if you don't want it to show.

UISwipeGestureRecognizer addGestureRecognizer to view in childview error

after some years I tried again to work with XCode to write some little apps for iOS.
My MainViewController contains these lines in viewdidload:
UIStoryboard* overviewStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *overviewController = [overviewStoryboard instantiateViewControllerWithIdentifier:#"Overview"];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:overviewController];
...
[self addChildViewController:nav];
[self.view addSubview:nav.view];
[nav didMoveToParentViewController:self];
the Controller behind the Overview contains the whole gesture recognition in view did load:
the property
#property (nonatomic, strong) UISwipeGestureRecognizer *swipeGestureUpDown;
viewdidload:
self.tableView.dataSource = self;
self.tableView.delegate = self;
// gesture recognizer top
self.swipeGestureUpDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedScreen)];
self.swipeGestureUpDown.numberOfTouchesRequired = 1;
self.swipeGestureUpDown.direction = (UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown);
[self.view addGestureRecognizer:self.swipeGestureUpDown];
and swipedScreen only an nslog:
- (void)swipedScreen:(UISwipeGestureRecognizer*)gesture
{
NSLog(#"somewhere");
}
THE overviewcontroller contains a tableView with custom cells.
The maincontroller passes this overviewcontroller as rootcontroller to a navigation, which should be slideUp if you swipeUp, and slideIn if you swipeDown. The maincontroller is calling the navigationcontroller with rootcontroller as you've seen above.
Nothing happens, no gesture is recognized, and in some tries it crashes with this message
unrecognized selector sent to instance
does somebody now what to do?
Answer to the question came up in the comments. Just consolidating it here.
There were a few issues.
First:
self.swipeGestureUpDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedScreen)];
#selector(swipedScreen) is missing : at end of swipedScreen which makes it unrecognizable as the definition of the function is - (void)swipedScreen:(UISwipeGestureRecognizer*)gesture
Second:
self.swipeGestureUpDown.direction = (UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown);
Having single gesture recognizer for two directions of swipe does not work. For details see this. You will need to have a dedicated gesture recognizer for each direction.
Third:
Most important of all was trying to add Up and Down direction swipes on UITableView which won't work as long as scrolling is enabled in UITableView as it has its own default actions to handle these swipes which prevents it from being handled manually. But if you have very limited content in the table and don't need scrolling, you can set scrollEnabled to false which will make UITableView stop using the gestures and forward the gestures higher up the responder chain. Refer scrollEnabled description here. (UITableView inherits from UIScrollView.)

Resources