How do I hide the mapview when I have an overlay on top of the mapview in iOS7? This snippet of code used to work in iOS6 but when i upgrade my app to iOS7 it cease to work.
NSArray *views = [[[self.mapView subviews] objectAtIndex:0] subviews];
[[views objectAtIndex:0] setHidden:YES];
Any suggestions or feedback?
With what incanus said with MKTileOverlay, it is like this in the view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *tileTemplate = #"http://tile.stamen.com/watercolor/{z}/{x}/{y}.jpg";
MKTileOverlay *overlay = [[MKTileOverlay alloc] initWithURLTemplate:tileTemplate];
overlay.canReplaceMapContent = YES;
[self.mapView addOverlay:overlay];
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(37.54827, -121.98857)];
self.mapView.delegate = self;
}
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
MKTileOverlayRenderer *renderer = [[MKTileOverlayRenderer alloc] initWithOverlay:overlay];
return renderer;
}
If you need control over how the overlay feeds the data, you need to subclass MKTileOverlay and override loadTileAtPath:result:
-(void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *, NSError *))result
{
NSData *tile = [self someHowGetTileImageIntoNSDataBaseOnPath:path];
if (tile) {
result(tile, nil);
} else {
result(nil, [NSError errorWithDomain: CUSTOM_ERROR_DOMAIN code: 1 userInfo:nil]);
}
}
The MKOverlay protocol requires boundingMapRect:, which should returns MKMapRect for the rectangular region that this overlay covers. However, I personally found that if I override it myself, it voids the prior canReplaceMapContent = YES setting as Apple probably does not like to show a blank gray map. So I just let MKTileMapOverlay handles it instead.
If your overlay is not actually tiles, then MKTileOverlay does not really apply. But I think you probably can fake it but always reporting nil data within loadTileAtPath:result:, and add your real overlay via another overlay. Another option would be just cover the whole world with black polygon overlay, but then the unsuspecting user would possibly be unknowingly streaming more data than he/she likes.
MapKit isn't really designed for direct access to the map view subviews outside of true overlays (e.g. turning off Apple's map underneath).
Two ideas:
Consider using the new iOS 7 MKTileOverlay class along with the canReplaceMapContent property. This has the effect of turning off Apple's underlying map.
Consider a similar but separate library such as the MapBox iOS SDK which can emulate the look of MapKit but has greater flexibility for styling (and also supports back to iOS 5).
I have no idea why you would want to do it but instead of counting the number of subviews, you should just ask the mapView for the number of overlays it has
if ([[mapView overlays] count] > 0)
{
....
}
Related
Im loading local html files, since iOS7 there is added white space on top in the UIWebView.(I cant post an image as i do not have enough points.)
image can be seen here- snap shot from iPhone simulator, uiwebview surrounded by black frame, the html content is grey, but there is white added above it
I have tried to adjust the zoom using
[webView stringByEvaluatingJavaScriptFromString:#"document. body.style.zoom = 5.0;"];
webView.scalesPageToFit = NO;
credit to: Srikar Appal
I also set tried to remove white spacing:
NSString *padding = #"document.body.style.margin='0';document.body.style.padding = '0'";
[webView stringByEvaluatingJavaScriptFromString:padding];
credit to: thenextmillionaire
still no luck. In the desktop chrome browser there is no whitespace. The html files are Google Swiffy files - containing html and JSON.
edit: updated Image
Try self.automaticallyAdjustsScrollViewInsets = NO; in ViewDidLoad.
ios 7 add 64px automatically for scroll view. (status bar and nav bar)
This problem only affects the UIWebView if it is the first subview of the parent view. One alternative way to work around this problem is to add another non-visible empty view to the parent view as the first view. In Interface Builder add a zero size subview and use the Editor->Arrange->Send to Back menu command.
If you're not using Interface Builder, but instead are subclassing the UIWebView, then it can be done by creating a UIView instance variable called scrollFixView and overriding the following methods:
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
if ([self superview].subviews.firstObject == self) {
_scrollFixView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
_scrollFixView.hidden = YES;
[[self superview] insertSubview:_scrollFixView belowSubview:self];
}
}
- (void)removeFromSuperview
{
if (_scrollFixView) {
[_scrollFixView removeFromSuperview];
_scrollFixView = nil;
}
[super removeFromSuperview];
}
I had the same problem so I tried a few things:-)
This worked for me, but correct me please if there is a better way.
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
if(self.navigationController.navigationBar.translucent == YES)
{
_webView.scrollView.contentOffset = CGPointMake(_webView.frame.origin.x, _webView.frame.origin.y - 54);
}
}
So basically you need to :
1) Add the UIWebView delegate method - webViewDidFinishLoad:
2) Then I setup an if statement to check if the translucent option is active.
The last one you only need to do of course if you give the user the option within your app.
The number after the _webView.frame.origin.y is just for my app. It may differ for you.
I solved this problem by simply setting a constraint on the WebView, setting the top space between it and the View top to 0, causing the NavBar to overlap the whitespace.
One alternative to Jeff Kranenburg's method is to subclass and override the UIWebView subclasses' UIScrollViewDelegate method scrollViewDidScroll:. This is only appropriate if scrolling is turned off for your subclass.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if ([[self superclass] instancesRespondToSelector:_cmd]) {
[super scrollViewDidScroll:scrollView];
}
[self fixUpScrollViewContentOffset];
}
- (void)fixUpScrollViewContentOffset
{
if (!CGPointEqualToPoint(self.scrollView.contentOffset, CGPointZero)) {
self.scrollView.contentOffset = CGPointZero;
}
}
I already got it .
here my logic code, When the application open the website you must get the size of your webview then set it on height
here my code
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) webpage.getLayoutParams();
p.height = webpage.getHeight();
// check if how long you need to set your height for webpage then set it :)
Log.e(" webpage.getHeight()", String.valueOf(webpage.getHeight()));
webpage.setLayoutParams(p);
Hope you will take my code and my answer to :) works on any devices even tabs too :)
I know this was already asked about a hundred times here, but I couldn´t find a suitable answer in the other questions.
My problem is the height of my UIProgressView. While everything worked as expected in iOS6, now in iOS7 nothing goes right.
I tried the following:
1.Setting the custom layout in the drawRect-Method:
Works like a charm in iOS6, but in iOS7 the progress is set to 100% from the beginning or the bar is very thin.
2.Setting the layout with the progressImage and trackImage property of the UIProgressView appearance
Also not working under iOS7. Here the bar progress is set to 100% from the beginning, too. Some people write that it should be possible this way, but I can not confirm that for iOS7.
3.Using initWithProgressStyle for initialization and then setting the frame of the progress view
Not working for me under iOS6 and iOS7. In iOS7 the bars are just very slim.
For me right now it is pretty frustrating because the bars are either at 100% or they are mega-slim. Can anyone give me a suggestion to reach the old layout of my progress views. I think it has to be possible because if I look at my Spotify app on the iPhone (iOS7 installed), the progress view looks like before.
Thank you very much!
Well, the problem is seams that iOS6 UIProgressView and iOS7 UIProgressView have different internal subviews structure. iOS6 progress view is a single view without child view (or some minor view), iOS7 progress view have few additional subview for drawing progress bar and background.
If you remove all subview of UIProgressView on iOS7 than you drawRect: method will work the same as before on iOS6, but you will be totally responsible about drawing your progress view content including progress bar and background.
- (id) initWithCoder: (NSCoder*)aDecoder
{
if(self=[super initWithCoder: aDecoder])
{
// Also you can setup height of your progress here
// self.frame = CGRectMake(0,0,100,yourHeight);
NSArray *subViews = self.subviews;
for(UIView *view in subViews)
{
[view removeFromSuperview];
}
}
return self;
}
I made kind of a workaround for this problem. I hope somebody can give a nice answer for a normal UIProgressView though.
I wrote a UIView subclass with round corners and a view inside of it which changes its size depending on the given progress. I only use colors for the background, but images would be possible too. Here´s the code:
#import "CustomProgressView.h"
#import <QuartzCore/QuartzCore.h>
#interface CustomProgressView ()
#property (nonatomic, retain) UIView *progressView;
#end
#implementation CustomProgressView
#synthesize progressColor,trackColor,progressView,progress;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.layer.cornerRadius = 5;
// clipsToBounds is important to stop the progressView from covering the original view and its round corners
self.clipsToBounds = YES;
self.progressView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, frame.size.height)];
[self addSubview:self.progressView];
}
return self;
}
-(void)setProgressColor:(UIColor *)theProgressColor {
self.progressView.backgroundColor = theProgressColor;
progressColor = theProgressColor;
}
-(void)setTrackColor:(UIColor *)theTrackColor {
self.backgroundColor = theTrackColor;
trackColor = theTrackColor;
}
-(void)setProgress:(float)theProgress {
progress = theProgress;
CGRect theFrame = self.progressView.frame;
theFrame.size.width = self.frame.size.width * theProgress;
self.progressView.frame = theFrame;
}
#end
I had a custom UIProgressView with it's own drawRect being updated from a background process.
In iOS6 all was working while in iOS7 the progressbar just did not update.
I have added layoutSublayersOfLayer right after the setProgress like this
[self.loadingProgress setProgress:pv.floatValue];
[self.loadingProgress layoutSublayersOfLayer:self.loadingProgress.layer];
and it worked like a charm.
I hope this helps someone.
Avoid a headache and use this excellent library:
YLProgressBar
Copy YLProgressBar.h and YLProgressBar.m from the YLProgressBar folder.
#import "YLProgressBar.h" in the file(s) you want to use the progress bar
Add your progress bar either by code or by xib
progressBar.type = YLProgressBarTypeRounded;
progressBar.progressTintColor = [UIColor greenColor];
progressBar.stripesOrientation = YLProgressBarStripesOrientationVertical;
progressBar.stripesDirection = YLProgressBarStripesDirectionLeft;
You have a nice, fully functional progress bar that supports width, height, modifications.
Having tried many methods I still haven't found a good and full-proof way of preventing the usual "maps" from being shown behind custom map tiles that I am using. Ultimately I want my app to have a map page consisting only of a custom map.
I am really looking for a solution that is pure iOS and doesn't require any 3rd party software but it would appear difficult.
I have tried 3 methods already:
Number 1, hiding the background map via it's view:
NSArray *views = [[[self.mapView subviews] objectAtIndex:0] subviews];
[[views objectAtIndex:0] setHidden:YES];
this however doesn't work on a certain new operating system coming out very soon! The whole screen goes blank. The Apple Developer Forum hasn't provided a solution either
Number 2, Using another blank overlay (e.g. MKCircle) to cover the background map. This works however when scrolling or zooming out quickly, sometimes the overlay flickers off and you can briefly see the background map behind so not ideal.
Number 3, and this is what I have been working on for a few days now is to simply prevent the user from zooming out. Most documented methods tend to use regionDidChangeAnimated or regionWillChangeAnimated, however these do not seem to suddenly stop the map zooming out when pinching - they wait until the pinch movement has finished before taking effect so again it means the background map can be viewed briefly.
So now I am stumped, unless of course I have missed something with these other two methods.
So any help would be much appreciated!
Add this:
-(void)viewDidAppear:(BOOL)animated
{
MKTileOverlay *overlay = [[MKTileOverlay alloc] init];// initWithURLTemplate:tileTemplate];
overlay.canReplaceMapContent = YES;
[map addOverlay:overlay];
overlay = nil;
}
-(void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *, NSError *))result
{
NSData *tile =nil;
}
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay: (id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKTileOverlay class]])
{
MKTileOverlayRenderer *renderer = [[MKTileOverlayRenderer alloc] initWithOverlay:overlay];
[renderer setAlpha:0.5];
return renderer;
}
}
It will replace the map content from the background. It worked very well in my case where I am adding an overlay on the whole map and hiding the real map from the user.
You can't do this in current releases without a third-party library like MapBox. However, the future OS release that you speak of lets you do this.
I have a transparent view with a rectangle drawn onto it using CoreGraphics.
When the camera launches the custom overlay view is above the shutter animation.
What you see is the standard camera shutter with the custom rectangle above it.
How do I get it to go in the right place, underneath the shutter animation? I've looked at other sample code but it's for OS 3.1 and doesn't seem to do anything differently.
Here's my code:
-(IBAction)cameraButton{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerCameraDeviceRear]){
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
//Add the OverlayView with the custom Rectangle
CGRect overlayFrame = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
OverlayView *overlayView = [[OverlayView alloc]initWithFrame:overlayFrame];
picker.cameraOverlayView = overlayView;
[overlayView release];
[self presentModalViewController:picker animated:YES];
[picker release];
}
}
On the iPad this problem doesn't exist, and the overlay view is behind the shutter animation by default. But on the iPhone, the overlay appears at front.
I've found a solution that worked for me.
You have to set your overlay view as a subview in this method:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (!viewController)
return;
UIView* controllerViewHolder = viewController.view;
UIView* controllerCameraView = [[controllerViewHolder subviews] objectAtIndex:0];
UIView* controllerPreview = [[controllerCameraView subviews] objectAtIndex:0];
[controllerCameraView insertSubview:self.overlayView aboveSubview:controllerPreview];
}
Hope it helps
Original source:
http://www.alexcurylo.com/blog/2009/06/18/uiimagepickercontroller-in-3-0/
You may not do anything else other than what you're already doing; if iOS decides to put your overlay view over the shutter, you'll just have to live with it (unless you want to risk getting rejected from the app store).
As an imperfect workaround, you could start your overlay with alpha=0 and then set alpha to 1 a second or two later. But there is no set time period that the shutter appears for before 'opening' (I think it depends on how long it takes to initialize the camera hardware), so sometimes your interface might not appear until late and sometimes might appear too early.
As of 4.3.3, the shutter animation is broken because elements are displayed on top, and then snap underneath when the animation starts. I've filed this as a Radar: http://openradar.appspot.com/radar?id=1204401
I answered a similar question here. What worked for me (in iOS 6) was setting the cameraOverlayView in navigationController:willShowViewController:animated.
- (void) navigationController:(UINavigationController*) navigationController willShowViewController:(UIViewController*) viewController animated:(BOOL) animated {
self.imagePickerController.cameraOverlayView = ...; // your camera overlay view
}
I have a MKAnnotationView that the user can drag around the map.
It is very difficult for a user to drag the pin. I've tried increasing the frame size and also using a giant custom image. But nothing seems to actually change the hit area for the drag to be larger than default.
Consequently, I have to attempt to tap/drag about ten times before anything happens.
MKAnnotationView *annView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"bluedot"] autorelease];
UIImage *image = [UIImage imageNamed:#"blue_dot.png"];
annView.image = image;
annView.draggable = YES;
annView.selected = YES;
return annView;
What am I missing here?
EDIT:
It turns out the problem is that MKAnnotationView needs to be touched before you can drag it. I was having trouble because there are a lot of pins nearby and my MKAnnotationView is very small.
I didn't realise MKAnnotationView needed to be touched before you can drag it.
To get around this, I created a timer that selected that MKAnnotationView regularly.
NSTimer *selectAnnotationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(selectCenterAnnotation) userInfo:nil repeats:YES] retain];
and the method it calls:
- (void)selectCenterAnnotation {
[mapView selectAnnotation:centerAnnotation animated:NO];
}
Instead of using a timer, you could select the annotation on mouse down. This way you don't mess with the annotation selection, and don't have a timer for each annotation running all the time.
I had the same problem when developing a mac application, and after selecting the annotation on mouse down, the drag works great.
Just came here to say this still helped me today, multiple years later.
In my application, the user can place a bounding area down with annotations and (hopefully) move them around freely. This turned out to be a bit of a nightmare with this "select first and then move" behaviour and just plain made it difficult and infuriating.
To solve this, I default annotation views to selected
func mapView(_ mapView: MKMapView,
viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "annotation")
view.isDraggable = true
view.setSelected(true, animated: false)
return view
}
The problem is that there is an annotation manager that resets all annotations back to deselected, so I get around this in this delegate method
func mapView(_ mapView: MKMapView,
annotationView view: MKAnnotationView,
didChange newState: MKAnnotationView.DragState,
fromOldState oldState: MKAnnotationView.DragState) {
if newState == .ending {
mapView.annotations.forEach({
mapview.view(for: $0)?.setSelected(true, animated: false)
})
}
It's a stupid hack for a stupid problem. It does work though.