I have an app that sends a notification every time the user approaches to a map on GMSMapView.
I want that when the user opens the notification (UILocalNotification) the marker will be already selected and its info window will be visible.
I've searched for a method in the delegate that do that (selectAMarker:/selectAMarkerInPosition: or something like that) but I haven't found something like that.
Anyone knows a method that do that/know how can I do that?
Is it even possible?
Thank you very much!
I think you like this method
-(BOOL) mapView:(GMSMapView *) mapView didTapMarker:(GMSMarker *)marker
{
NSLog(#"try");
return YES;
}
Dont Forget to call delegate
#interface ViewController : UIViewController <GMSMapViewDelegate>
and
yourmapView_.delegate = self;
Related
Let's say, we have simple application with one view controller and single bar button item which will open CNContactPieckerViewController. So, I enter into picker view, choose contact, enter into details view. If there is implementation for contactPicker:didSelectContactProperty: delegate method in my view controller, pressing a call button will call delegate method and CNContactPickerViewController will be dismissed and for making a call I need to handle everything manually in delegate method. But if there is no implementation for contactPicker:didSelectContactProperty: delegate method, call will be made (based on what kind of call did you choose - GSM, WhatsApp or something else) but CNContactPickerViewController won't be dismissed.
Question
is there any possibility dismiss CNContactPickerViewController without losing functionalities for handling calls.
you can add observer while call connected and disconnected for both states and can dismiss the CNContactPickerViewController controller. but i would suggest use contactPicker:didSelectContactProperty : delegate to achieve this functionality.
1.import CallKit framework
#import <CallKit/CXCallObserver.h>
#import <CallKit/CXCall.h>
2.Conform class to CXCallObserverDelegate protocols.
3.Make strong reference to CXCallObserver object like as
#property (nonatomic, strong) CXCallObserver *callObserver;
4.Initialize callObserver object while you are presenting CNContactPieckerViewController like as
CXCallObserver *callObserver = [[CXCallObserver alloc] init];
[callObserver setDelegate:self queue:nil];
_callObserver = callObserver;
5.Finally implement the delegate method
-(void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
if (call.hasConnected) {
NSLog(#"********** voice call connected **********/n");
} else if(call.hasEnded) {
[(your CNContactPieckerViewController object) dismissViewControllerAnimated:YES completion:nil];
NSLog(#"********** voice call disconnected **********/n");
}
}
Is it possible to intercept all user actions like tap, swipe, enter text, etc. on all windows of my app?
Like I said in the comments, subclass UIApplication and override the instance method sendEvent:.
From the documentation for the UIApplication class, -sendEvent: method:
Discussion
If you require it, you can intercept incoming events by
subclassing UIApplication and overriding this method. For every event
you intercept, you must dispatch it by calling [super sendEvent:event]
after handling the event in your implementation.
So, it would look like this:
CustomUIApplication.h:
#interface CustomUIApplication:UIApplication
- (void)sendEvent:(UIEvent *)event;
#end
CustomUIApplication.m:
#implementation CustomUIApplication
- (void)sendEvent:(UIEvent *)event
{
// ...Do your thing...
[super sendEvent:event];
}
#end
Of course, you need to make sure your subclass is used instead of the default UIApplication. Here is a Stack Overflow answer on how to do it in Objective-C, and here in Swift.
I want to disable the swipe back gesture that allows users to go back.
I've tried:
if ([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
and all sorts of other code I've found online, but none of them work at all? I'm using iOS 8.3.
Is there a way to disable this all together? Thank you.
First of all as #Fogmeister said in comments, you need to have a very good reason to remove this native function to your app.
Now, having said this, the solution:
SwipeBack and JRSwizzle (Needed by SwipeBack)
You can use this in a single ViewController in which you want to remove the functionality or you create a custom class for UINavigationController and you use it there:
#import <SwipeBack/SwipeBack.h>
- (void)viewWillAppear:(BOOL)animated
{
//For a single viewcontroller
self.navigationController.swipeBackEnabled = NO;
//If you are in the custom class
self.swipeBackEnabled = NO;
}
Hope it helps.
Hi I am working on a iPad app and got a requirement to dismiss all popovers (if any) when app goes in background.
I did some study online and didn't find a simple way to do it. I'd like to share some my idea here and see if there are a better way to do it.
1, Dismiss popovers in didEnterBakcground in delegate. Seems not practical since we have to add all popovers reference in.
2, Go through all views recursively in current window to find popover view by (class = _UIPopoverView). It is seems a bit hacky and dangerous.
3, Set up UIApplicationDidEnterBackgroundNotificationgroundNotification in each object who own popovers and dismiss them. This seems reasonable, but really troublesome if there are hundreds of popovers in your app.
4, How about add a category method say -(void)dismissWhenAppWillEnterBackground; and register notification.
Or there is easier way to do it?
Here is a drop-in category on UIPopoverController that does what you're asking.
Basically the category swizzles initWithContentViewController: so that it can track live UIPopoverController instances in a NSHashTable (which doesn't itself hold the contained UIPopoverControllers alive since it keeps weak references to them.) It also monitors for UIApplicationDidEnterBackgroundNotification, and when this arrives it iterates the collection of live UIPopoverControllers and dismisses any that are showing.
It might be nice to extend this to implement the "never allow two popovers to show at once" rule that Apple has.
I'm not a huge fan of method swizzling in production apps but this seems pretty safe.
No special instructions for use. Just include the category in your project and use your UIPopoverControllers normally.
#import <objc/runtime.h>
#interface UIPopoverController (autodismiss)
#end
#implementation UIPopoverController (autodismiss)
static NSHashTable* ts_popoverHashTable;
+ (void) load
{
SEL originalSelector = #selector(initWithContentViewController:);
SEL replacementSelector = #selector(ts_initWithContentViewController:);
Method originalMethod = class_getInstanceMethod( [UIPopoverController class], originalSelector);
Method replacementMethod = class_getInstanceMethod( [UIPopoverController class], replacementSelector);
method_exchangeImplementations(originalMethod, replacementMethod);
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector( applicationDidEnterBackgroundNotification: )
name: UIApplicationDidEnterBackgroundNotification
object: nil];
}
- (id) ts_initWithContentViewController: (UIViewController*) contentViewController
{
UIPopoverController* pc = [self ts_initWithContentViewController: contentViewController];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
ts_popoverHashTable = [NSHashTable weakObjectsHashTable];
});
[ts_popoverHashTable addObject: pc];
return pc;
}
+ (void) applicationDidEnterBackgroundNotification: (NSNotification*) n
{
for ( UIPopoverController* pc in ts_popoverHashTable )
{
if ( pc.isPopoverVisible )
{
[pc dismissPopoverAnimated: NO];
}
}
}
#end
I may have a better answer, which is add a category method -(void)dismissWhenAppWillEnterBackground to UIPopoverController and register UIApplicationWillEnterBackgroundNotificationgroundNotification.
Write a protocol with a couple of optional methods:
- (void)appWillEnterBackground;
- (void)appWillBecomeActive;
Make your view controllers to implement it and then in your app delegate, access your root view controller, check if it responds to those methods and invoke them when the app is going to background and becoming active.
You should be able to obtain the root view controller easily. If you have a hierarchy of view controllers you may need to forward the call.
Add your popover dismissal code in appWillEnterBackground, for instance.
Create a uiviewcontroller base class for all the view controllers in the application.
Add an array which contains the references to the popover views in the particular viewcontroller
Maintain a reference to the current viewcontroller iin app delegate .
When app enters in background, get the current viewcontroller and travers the popover array and dismiss all the popovers.
After calling MKMapView's setCenterCoordinate:animated: method (without animation), I'd like to call selectAnnotation:animated: (with animation) so that the annotation pops out from the newly-centered pushpin.
For now, I simply watch for mapViewDidFinishLoadingMap: and then select the annotation. However, this is problematic. For instance, this method isn't called when there's no need to load additional map data. In those cases, my annotation isn't selected. :(
Very well. I could call this immediately after setting the center coordinate instead. Ahh, but in that case it's possible that there is map data to load (but it hasn't finished loading yet). I'd risk calling it too soon, with the animation becoming spotty at best.
Thus, if I understand correctly, it's not a matter of knowing if my coordinate is visible, since it's possible to stray almost a screenful of distance and have to load new map data. Rather, it's a matter of knowing if new map data needs to be loaded, and then acting accordingly.
Any ideas on how to accomplish this, or how to otherwise (reliably) select an annotation after re-centering the map view on the coordinate where that annotation lives?
Clues appreciated - thanks!
I ran into the same problem, but found what seems like a reliable and reasonable solution:
Implement the delegate method mapView:didAddAnnotationViews:. When I tried selecting the annotation directly within the delegate method, the callout dropped with the pin! That looked odd, so I add a slight delay of a half-second.
-(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
[self performSelector:#selector(selectInitialAnnotation)
withObject:nil afterDelay:0.5];
}
Select the initial annotation as you'd expect, but calling selectAnnotation:animated;
-(void)selectInitialAnnotation {
[self.mapView selectAnnotation:self.initialAnnotation animated:YES];
}
It seems that selectAnnotation:animated: is not called under some conditions. Compare with MKMapView docs:
If the specified annotation is not onscreen, and therefore does not
have an associated annotation view, this method has no effect.
A more consistent way than using a fixed timeout is to listen to the regionDidChange callback. Set the center coordinate of the map to the desired annotation, and when the regionDidChange method is called, then select the annotation in the center, to open the callout.
Here's a little video I took of the thing running randomly between 5 pins.
First, goto the center coordinate of the annotation. Let's say the annotation object is named thePin.
- (void)someMethod {
[map setCenterCoordinate:thePin.coordinate animated:YES];
}
Then in the regionDidChange method, select this annotation.
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
[map selectAnnotation:thePin animated:YES];
}
Well just FYI, this is what the docs say,
If the specified annotation is not onscreen, and therefore does not
have an associated annotation view, this method has no effect.
So if I want to call setCenterCoordinate:animated: or setRegion:animated: and then I want to select the annotation by calling, selectAnnotation:animated: , the annotation won't get selected and the callout won't appear beacause of the exact same reason mentioned above in docs, So the way it would be great to have something like, setCenterCoordinate:animated:ComletionBlock but its not there..! The way that worked for me is as below,
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{
[self.mapView setCenterCoordinate:location.coordinate animated:YES];
} completion:^(BOOL finished) {
[self.mapView selectAnnotation:location animated:YES];
}];
This will give you a completion block and u can use that to select the annotation.
What has worked for me was calling selectAnnotation:animated: from the mapView:didAddAnnotationViews: method:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views;
{
[mapView selectAnnotation:[[mapView annotations] lastObject] animated:YES];
}
Apple documentation on the same here.
Note that I only had one annotation on the map so [[mapView annotations] lastObject] was fine for my purposes. Your mileage may vary.
I was having a similar problem. I was using the default MKPinAnnotationView with animatesDrop:YES. After I added the annotations to the MKMapView, I was doing this:
[mapView selectAnnotation:[mapView.annotations objectAtIndex:1] animated:YES]
which in the logic of my program, should select the nearest annotation. This wasn't working. I figured out the reason: the annotation view was not on the screen at the time of this select call, because of the pin drop animation. So all I did was set a timer to select the annotation a second later. It's a hack, but it works. I'm not sure if it'll work in every situation though, for instance on a 3G vs. 3Gs. It'd be better to figure out the right callback function to put it in.
selectTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:#selector(selectClosestAnnotation) userInfo:nil
repeats:NO];
- (void)selectClosestAnnotation {
[mapView selectAnnotation:[mapView.annotations objectAtIndex:1]
animated:YES];
}
I've found several problems with all the solutions I saw for my problem (I want to select a annotation when I added it from viewDidAppear):
Using the mapView:didAddAnnotationViews: delegate method.
This didn't work for me because if the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.
Using the mapViewDidFinishLoadingMap: delegate method.
This didn't work for me either because the map is cached now, so the method is called only once, the first time.
Solution:
John Blackburn was very close but without the mapView:didAddAnnotationViews: method. I just call my own selectAnnotation: method, before I add the annotation, with a delay:
[self.mapView addAnnotation:ann];
[self performSelector:#selector(selectAnnotation:) withObject:ann afterDelay:0.5];
And this is what I do in my selectAnnotation: method:
- (void)selectAnnotation:(id < MKAnnotation >)annotation
{
[self.mapView selectAnnotation:annotation animated:YES];
}
Tested on two iPhone 3GS (iOS 5 and 6) and an iPhone 5.
I was having similar difficulty, but was actually not able to make this method work at all, let alone inconsistently.
Here is what I found to work. Maybe it will work for you too:
How to trigger MKAnnotationView's callout view without touching the pin?