MKMapView visibleMapRect not completely visible - ios8

I have a MKMapView that marks a specific region using MKPolygon. I want the map to have minimum zoom factor that fits inside the mapView / screen, means completely visible.
To achieve this I tried the following
MKPolygon *overlay;
[[self mapView] setVisibleMapRect:[overlay boundingMapRect] edgePadding:UIEdgeInsetsMake(16, 16, 16, 16) animated:YES];
Unfortunately my mapView does not set the whole rect visible. The rect fills the map instead of fitting.
This is what I need vs. what I got:
vs.
How can I achieve my goal?

I finally found the answer. It didn't have to do anything with the MKMapView. I just started using size classes in Xcode 6 and so there was my problem located.
The call to [MKMapView -setVisibleMapRect:edgePanning:animated:] was made too early, namely in the -viewDidLoad method.
When I was thinking of Annas comment and probed a bit I discovered the she was totally right. Then it dawned on me that I should call the mapView method later, e.g. -viewDidLayoutSubviews.
As I moved the one line it worked just perfectly! Thanks, Anna!

You want to pass [overlay boundingMapRect] through -[[self mapView] mapRectThatFits:].
A map rectangle that is still centered on the same point of the map but whose width and height are adjusted to fit in the map view’s frame.

If -setVisibleMapRect is set in -viewDidLayoutSubviews, then the visible maprect will be reset when the map is zoomed in/out.
My enhancement is: define a class level flag and invoke -setVisibleMapRect only if that flag is on. Once -setVisibleMapRect is invoked, turn the flag off. From now on, zooming in/out the map won't reset the visible maprect. Here is my code in Swift:
var resetMapRect = true // class level variable
override void viewDidLayoutSubviews() {
if resetMapRect {
// set visible maprect here
resetMapRect = false // no more setting maprect
}
}

Related

Why does my view debugger show a different layout and how can I match it?

Why does my view debugger show a different value and how can I make my app match it?
My view debugger is showing this (this is the correct view):
But my iPhone is incorrectly showing this:
I spoke to someone I know and they said that the debugger lays out the subviews again. How can I trigger this same thing in my application?
This is what my hierarchy looks like:
---UICollectionView
----UICollectionViewCell
-----UIStackView (vertical, scale to fill)
------UILabel (hugging and compression are 1000 for vertical and horizontal)
-------UIImageView (aspect fill, hugging and compression are 750 for vertical and horizontal)
If I begin my app in portrait, all my views layout correctly. If I begin my app in landscape, then I have that incorrect overlay that you see in the image. Any help?
Thanks!
You should set imageView.clipToBounds = true (or any other UIView's) either in code or in IB.
Apple Documentation Reference
A Boolean value that determines whether subviews are confined to the
bounds of the view. Setting this value to true causes subviews to be clipped to the bounds of the receiver. If set to false, subviews whose frames extend beyond the visible bounds of the receiver are not clipped. The default value is false.

Make control horizontally aligned in Xcode

I'm trying to horizontally align my UILable for iPhone screen, by there is no x or left in mylable properties.
How can center-align it on the screen?
The property you are looking for is the frame or center property. In this case, center is probably your best bet. All you need to do is something like this:
CGPoint newCenter = myLabel.center;
newCenter.x = self.view.center.x;
myLabel.center = newCenter;
or
myLabel.center = CGPointMake ( self.view.center.x, myLabel.center.y );
There are more advanced ways of maintaining center alignment using autolayout, or the autoresizingmask. If you are interested in learning about them (autolayout being the most robust and in some ways user friendly) I recommend watching the WWDC 2012 sessions about Auto Layout
The best way to determine what types of properties an object has is to use the class reference. When you first look at UILabel class reference you'll see there are no properties for adjusting the view of the label.
But if you look at the very top you can see where UILabel inherits from. You can see it inherits from UIView, which makes sense to look at for what you need since you're asking how to adjust the labels position on the screen (the view).
Click on UIView and you'll be taken directly to it's reference. Scroll down until you see a list of "Tasks" under which there are several categories, each with a number of properties. You're interested in the "Bounds and Frame Rectangles" category in which there is a property called "frame".
By now we've determined we can set the UILabels frame via myLabel.view.frame
But you may be wondering how to set a frame and so you need to click on 'frame' in the class reference you've been looking at.
You'll see that frame is of type CGRect, so you can use CGRectMake to set the frame. But now you're asking how do I do that, so we click on the CGRect reference.
And you can see that CGRect is a struct made of a CGPoint and a CGSize and if necessary please look those up as well to understand how they are defined.
I took this time to explain the flow of using the docs so that you can do it for yourself in the future. You can survive for awhile on the help of others, but eventually you'll need to just dive into the docs and figure it out for yourself.
In short, you can set the position of your label using the following:
myLabel.frame = CGRectMake(self.view.frame.size.width/2-someWidth/2, self.view.frame.size.height/2-someHeight/2, someWidth, someHeight);
Where someWidth and someHeight are custom variables of float type and self.view is some superview you want to center the label in.
I did not test the code but believe this should work. Good luck to you.
This works:
mylabel.center = CGPointMake(CGRectGetMidX(self.view.bounds), mylabel.center.y);

UIimages move to the center when going to another view

I have a drag n drop kind of app. Where you can select images and drag them anywhere on the screen!
The problem I'm running into is, when you move to another view, all the images reset to the center, when I return back.
For example if I press a button to take me to screen 2, all the "dragged" images I just did, will move back to the center.
This only happens when I have AUTOLAYOUT enabled :(
I have all my images start out in the center, so I'm guessing its something with autolayout...
Any ideas ?!
Here's an example of my drag image code.
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
Your drag and drop code is manually configuring the layout by mutating the view.center property.
Once auto layout is enabled, auto layout takes responsibility for setting up the layout, so it takes exclusive responsibility for setting view.center, view.bounds, and view.frame (which is actually just calculated from center and bounds).
So once auto layout is enabled, although you can still set view.center manually, auto layout will clobber whatever you do the next time it calculates the layout that satisfies the constraints you have in place.
So how do you update your code to work with auto layout? If you want to use auto layout, what you need to do is modify your handlePan: method so that instead of modifying view.center it modifies whichever auto layout constraint is being used to calculate view.center. The details of this will depend on your constraint configuration. But if we assume, for example, that there is an NSLayoutContraint topSpaceConstraint that sets the view's top space to the superview, and another NSLayoutConstraint leftSpaceConstraint that sets the view's left space to the superview, then you could produce the same effect as
view.center = CGPointMake(view.center.x + translation.x,
view.center.y+ translation.y);
by instead doing something like
topSpaceConstraint.constant = topSpaceConstraint.constant + translation.y;
leftSpaceConstraint.constant = leftSpaceConstraint.consant + translation.x;
[view setNeedsLayout];
[view layoutIfNeeded];
The first two lines update the constraints. The last two lines cause the auto layout system to recalculate the resulting frame and apply it right away, rather than waiting until the next turn of the run loop.
Alternatively, you might be able to get away with updating the frame manually when you get UIGestureRecognizerChanged events, and then making the result "permanent" by updating the constraints as shown above only once you get UIGestureRecognizerEnded event. That would be more performant, since during the many UIGestureRecognizerChanged events you'd be updating the frame directly rather than relying on the auto layout system to do it.

Resize custom NSView inside NSScrollView by setFrameSize triggers drawRect

So I have this simple custom NSView-inside-NSScrollView setup, constructed in awakeFromNib programmatically, exactly like in Apple's ScrollView Programming guide.
The custom view (documentView of NSScrollView) initially has a certain frame-size and the scroll-position remains at the origin of the custom-view. No user-initiated and no programmatic scrolling is going on.
Now, from a different thread, by issueing a performSelectorOnMainThread, I change the framesize of the custom-view by invoking setFrameSize(newSize) on the custom view, only making it bigger in height. Origin and width stay the same.
The question now is, why am I getting the drawRect: invoked, presumably by cocoa, on the custom view with a dirty rect of (0, 0, viewwidth, viewheight) ?
I think this is strange, at least it is not optimal because there is a redraw-request in an area where nothing has changed. The height was extended from 10000 to 10300 for example, which is not relevant to the rect being shown at that moment in the visible rect (0, 0, viewwidth, viewheight).
From the class-reference of NSView, I was assuming that setFrameSize: does not cause drawRect: to be triggered, as long as you don't issue a setNeedsDrawInRect: or similar.
So, does anyone know why, in the case of being contained in a NSScrollView, this drawRect: call to the contained view is made by Cocoa ?
And which object (NSScrollView, NSClipView, NSNotificationCenter...) is issueing this call ?
Any hint greatly appreciated,
Joerg

How do I fix the height of my view after collapsing NSSplitView?

I've got an NSSplitView with an NSScrollView in the bottom view. The problem is when I collapse, and then re-open (un-collapse) the bottom view, the height of the scroll view is beyond the height of that bottom view so the top part of the scoll view is being clipped. I've got my scroll view and my split view set to autoresize in all directions in IB. Do I need to adjust the height of that scroll view after the un-collapse or am I setting a resizing property wrong, or something else? Below is a before and after image of what the clipping looks like.
Before Collapse:
After Collapse and re-open (notice the scroll bar in the bottom view is clipped)
The problem stems from the fact that cocoa autoresizing rules work by scaling deltas from the previous state to the current state. If any of the margins go to 0 they'll never scale back up as the view grows because of the multaplicative nature of the scaling.
The typical approach to working around this is to use the NSSplitView delegate methods to prevent the split view from getting to small and then have it snap shut - which internally keeps the collapsed view at the minimum size.
Here's a link to the split view documentation.
Also, if you think about the user experience, your views probably look really awful when they're sized down below a certain point - views probably start overlapping, and becoming too small to show their content. Adding this snapping-collapsing behavior addresses both problems.
If you want to see an example of this, Mac OS X's Mail.app snaps its inline message view closed when it gets to a certain height. You should mimic that behavior.
I have the same problem. Fixed it using BWToolkit's split views, which allow you to determine the maximum and minimum height for each view.
You could "reset" things via NSUserDefaults, possibly.. There are keys for such things as NSSplitView Subview Frames, etc, to which you can assign coordinates, a la 0.000000, 0.000000, 0.000000, 720.000000, NO
While Jon Hess could describe the problem well (as soon as a subview's width becomes 0 the autosizing information gets lost for auto-width elements), the solution is not really given for all cases.
Constraining the width did not help in my case, as the subview can be collapsed.
I managed to achieve an acceptable solution, by implementing the splitView delegate method -splitviewWillResizeSubviews: to maintain a minimum width by setting the subview to hidden instead of shrinking it to zero:
- (void)splitViewWillResizeSubviews:(NSNotification *)notification {
NSUInteger divider = [[[notification userInfo] valueForKey:#"NSSplitViewDividerIndex"] intValue];
NSView *subview = nil;
if(divider == SPLITVIEW_DIVIDER_SIDEBAR) {
subview = (NSView*)[self.splitView.subviews objectAtIndex:SPLITVIEW_SIDEBAR_INDEX];
}
if(subview) {
if(subview.frame.size.width < SPLITVIEW_MINIMUM_SIDEBAR_WIDTH) {
CGRect correctedFrame = subview.frame;
correctedFrame.size.width = SPLITVIEW_MINIMUM_SIDEBAR_WIDTH;
subview.frame = correctedFrame;
subview.hidden = YES;
} else {
subview.hidden = NO;
}
}
}

Resources