How to make UIPopoverController change its arrow direction automatically - ios

I've encountered strange problem in UIPopOverController.
Normally, I present popover this way:
[popoverVC presentPopoverFromRect:sender.frame
inView:sender.superview
permittedArrowDirections:UIPopoverArrowDirectionLeft
animated:NO];
However, sometimes there is no enough space on the right side of the button (sender) and the popover is displayed below the button with UIPopoverArrowDirectionUP. It make sense - if popover can't be displayed properly, controller trys to display it with different arrow.
But when I moved my button (sender) about 10 pixels to the left, popover doesn't behave this way. There is still no enough space to display it properly but it doesn't change its arrow although popover is ~20px wide so it's way too small.
Is there any way to say popovercontroller: "If popover does not have enough space to display all content, change arrow direction" ?

Why not use UIPopoverArrowDirectionAny, which Apple recommends anyway, or, if you absolutely must, UIPopoverArrowDirectionLeft | UIPopoverArrowDirectionUp? The exact orientation of the popover's arrow seldom matters that much.
If that doesn't work, I guess the only choice is to do some math in the action method. (Disclaimer: I've never tried this, but I don't see why you couldn't do it if you absolutely had to.) First convert the sender's bounds to the top-level view's coordinate space. Depending on the specifics of your app's view hierarchy, you may need to convert the bounds/frame to/from an appropriate view. Unless you've got a something weird going on, this should work:
[self.view convertRect:[sender bounds] fromView:sender]
Then check whether the resulting rectangle is more than X points away from the right edge of the screen, where X is the width of the popover (including the black border and arrow). If there is enough room, present the popover as you say above. If not, use UIPopoverArrowDirectionUp instead.

Related

What to do when pushing a view that's smaller than the underlying one?

I have an iPhone app that's presenting a typical master/detail view, starting with a tableview. When a row is tapped, I push the view controller for that row (which is loaded from a storyboard). The views for each row are all of various sizes, some containing only a few controls.
I find that iOS doesn't push the underlying view away smoothly; if the incoming view is smaller than the current one, a fragment of the current one moves awkwardly only partly to the left and remains visible before blinking out.
Watch the bottom here.
That bottom bar is not a toolbar, by the way. It's laid out on the main view explicitly, like anything else.
Here's how the incoming view is invoked (tabBoard is a storyboard):
if let singlePageController = tabBoard.instantiateViewController(withIdentifier: tabName) as? AtomViewController
{
navigationController?.pushViewController(singlePageController, animated: true)
}
I could make an unnecessarily large incoming view, but this is a hokey workaround considering that I don't know the resolution of the user's device or of future devices. And there's no way I can see of making the topmost view auto-resize to the safe area. I've set autoresizing to expand the view, but no dice.
I've also tried setting the incoming view controller's view frame to equal the current safe area. The frame size is changed (to one that indeed looks full-screen), but the transition is still messy.
What gives?
Please check these boxes in the ViewController setting.
Hide bottom bar on push
Use Full Screen
In the end, the solution was to make sure the incoming view's background color wasn't set to "clear." Setting all the views' backgrounds to black eliminated the problem.
This would have been more obvious if iOS had simply left the underlying view in place, so we could see it through the "clear" overlying view. The fact that the underlying view moves up and partially off the screen to the left... and then stalls and just blinks out... doesn't seem intended.

Finer grained UIView rotation in a UIViewController

I have a UIViewController that overlays controls on a view presenting what the camera sees. I have a couple of scenarios I would like to allow.
For the iPad, I want to keep the controls on the right most edge of the device, by your right thumb, no matter what the device's rotation. The controls should rotate their content so that their top is always upwards (away from the ground). I don't want the camera view to rotate at all, because that would just be silly – its position & size should stay the same and its contents shouldn't rotate either.
For the iPhones, I want to keep the controls at the bottom of the device's screen, by to the home button, wherever the home button actually is. The controls should rotate their content so that up is always pointing upwards. Again, I don't want the camera view's frame or content to take part in any view rotation animation at all.
I'm using auto-layout.
I'm wondering if there is any way to describe some or all of this in a storyboard. In particular, it'd be great to be able to describe that some view positions need to autorotate (ie, the controls, on iPad), but that other views don't (the camera view).
A question from 2011 indicates this wasn't possible at the time, but perhaps things have moved on since then? If it's not directly supported, can you suggest an approach and are there some sensible places to be hooking in to autorotation to achieve this?
Ok, this isn't quite a complete answer, but I tried a few things which look promising.
First, you can create a separate set of constraints for portrait vs. landscape using the size specifiers: landscape is w Regular, h Any; portrait is w Any, h Regular (I think -- double-check these) This is accessible via the pop-up control in the bottom-center of the storyboard view. By installing different constraints for portrait and landscape, it should be possible to scale the width and height of your controls' container view so it appears to be in a constant position w.r.t. device orientation; in other words, the container doesn't actually counter-rotate -- it scales so it effectively looks like it has counter-rotated.
I got this close to working. It looks like it's doing the correct thing in the storyboard view, but when I actually run it, I get debug messages about conflicting constraints. Not sure how to fix this, but maybe play with the constraint priorities? That sometimes helps.
A second thing I (partially) tried was creating a custom container view class which counter-rotates itself to the correct position based on the device orientation (in the UIDevice class). You implement this by overriding layoutSubviews. For each orientation, you define a transform which puts it in the correct position, and set the view's transform property.
Another possible solution is to override updateConstraints in your view controller and add/remove constraints to position/scale your container to the correct place for each orientation.
For all of these, the idea is that you "force" the container to be in the correct place, but leave the subviews (the actual controls) alone. The controls should do the right thing if their constraints are independent of the specific orientation of the container view.
So, those are some ideas anyway... if they lead you to an actual solution, could you post it? I anticipate having a need for this myself.

ios: Get size of parent popover from child viewController?

I have a viewController which is presented inside a popover. I'm trying to figure out how to size the viewController's view (viewController's self.view) to match the popover.
I would think the view would automatically be size to fit into the popover; for some reason, it isn't. The view's frame is the size of the entire screen, for some reason.
How do I either: detect the size of the surrounding view controller so I can resize the view; or, cause the view to automatically size itself to the popover?
Note: This is a class which is embedded into someone else's app, so I have no control over the popover size itself.
Figured out what I was doing wrong: I was looking at the frame size in the viewDidLoad method. It's not always valid at that point, for some reason.
The correct place to check the frame size is viewDidAppear; it seems to be valid there. Admittedly, though, I haven't checked this in every single corner case, so YMMV.
Override contentSizeForViewInPopover for your view controller and return the size you need the popover to have. So, it somehow works the other way around, the contained view controller lets the popover know how large should it be. The popover is 320px x 1100px by default I think. If you overwrite the method I mentioned above, you'll make it as large as you want it to be, even if the code is embedded in an app and you don't have control over it.

iPad - redrawing the arrow direction of a visible UIPopOverController

I have an object that is drawn near the first third of the screen. Imagine that there are 300 pixels between the top of the object (an image, for example) and the top of the screen.
When I show a popover, lets say, with 200x200 pixels and specify arrow direction as any, the popover may appear over the object, that is, between the object and the top of the screen.
If the popover for some reason, loads another view and is resized to more than the available space between the object and the top of the screen, it will not be able to fit there. iOS solves that, forbidden the popover to resize to more than possible. The result is that the content gets truncated and probably the popover will move a little bit and the arrow will point to nowhere.
Is there any way to make a popover that has increased to recalculate the best arrow direction?
thanks.
You can calculate the size of your needed future content (contentView) and compare it to your threshold and force the arrow direction. you may need to experiment with your threshold as the size of a content view isnt the entire height of the popover.
int popoverArrowDirection 15;//any
int threshold = 300;
if(viewThatWillbeInserted.frame.size.height>threshold)popoverArrowDirection = 1;//up
[myPopoverController presentPopoverFromBarButtonItem:myBarButton permittedArrowDirections:popoverArrowDirection animated:YES];
If you cant know this height before then you don't have many options.
The limitations of popovers drove me to mock-up a popover as Custom ViewController (like the page popover you see when you slide through the pages in a ibook; thats not an actual popover component). This would mean that if you had a down arrow, and pushed on a new bigger view you could even animate the view spinning around the origin of arrow tip or any other stupid animations..
I have only done this in enterprise apps, and am unsure weather this is app-store safe!
What happens if you dismiss the popover and then re-present it after the content size has changed?

UIView coordinate transforms on rotation during keyboard appearance

iPad app; I'm trying to resize my view when the keyboard appears. It amounts to calling this code at appropriate times:
CGRect adjustedFrame = self.frame;
adjustedFrame.size.height -= keyboardFrame.size.height;
[self setFrame:adjustedFrame];
Using this technique for a view contained in a uisplitview-based app works in all 4 orientations, but I've since discovered that a vanilla uiview-based app does not work.
What happens is that apparently the uisplitview is smart enough to convert the coordinates of its subviews (their frame) such that the origin is in the "viewer's top left" regardless of the orientation. However, a uiview is not able to correctly report these coordinates. Though the origin is reported as (0,0) in all orientations, the view's effective origin is always as if the ipad were upright.
What is weird about this is that the view correctly rotates and draws, but it always originates in the literal device top left. How can I get the view to correctly make its origin the "top left" to the viewer, not the device's fixed top left? What am I missing? Please, for something so trivial I've spent about 6 hours on this already with every brute force technique and research angle I could think of.
This is the original source which doesn't work in this case:
move up UIToolbar
OK, I don't know what the ACTUAL answer is to the original question, but I can say with certainty that one way to resolve the issue is to always ensure that you don't manipulate a viewController's view directly. Always wrap your view inside a container view inside the main "view", then have that container view adjust its position etc as needed. Works exactly as the splitview does, probably because in both cases now the view in question is a subview of the main "view". What a relief!

Resources