Map Issue for MKAnnotationView in iOS 7 and iOS 8 - mkmapview

I use iOS SDK 8 and having problem to display annotation for certain locations but it is working fine for some locations.
For those failed location, it keeps looping at if (pinView == nil) and the app will quit unexpectedly, any idea for the root caused?
- (void)populateAllLocation {
self.locations = [selectedCountry_.locations allObjects];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"latitude != nil && longitude != nil"];
self.locations = [[selectedCountry_.locations filteredSetUsingPredicate:predicate] allObjects];
[mapView_ addAnnotations:locations_];
[self centralizeMap];
}
-(MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:MKUserLocation.class]) {
static NSString *PinIdentifier = #"UserLocationPin";
MKAnnotationView *pinView =
[mapView_ dequeueReusableAnnotationViewWithIdentifier:PinIdentifier];
if (pinView == nil) {
pinView =
[[[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:PinIdentifier] autorelease];
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"mylocation_pin"];
}
[self populateAllLocation];
return pinView;
}

Your populateAllLocation is triggering display of the annotation views, but the delegate method itself calls populateAllLocation so you have a loop. You should only be returning annotation views in the delegate, not altering the map.

Related

NSFetchedResultsController refresh for fetching new data

Please direct me to the right way.
I implemented this code to fetch my objects:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSPredicate *predicate = nil;
if (self.selectedCategory)
{
predicate = [NSPredicate predicateWithFormat:#"ANY category_ids.category_id == %#", self.selectedCategory];
}
_fetchedResultsController = [EyeArtist fetchAllGroupedBy:nil withPredicate:predicate sortedBy:#"artist_id" ascending:NO delegate:self];
return _fetchedResultsController;
}
So when app run the at first time fetch works without predicate, so at second time I need new fetch with predicate.
I tap on the button and set string self.selectedCategory, but I don't know how to refetch data from - (NSFetchedResultsController *)fetchedResultsController;
So I suppose it has to be like execute new request for fetchedResultsController instance.
After changing the search criteria, you have to set the instance variable self.fetchedResultsController to nil,
so that the next call to the "lazy getter" function creates a new FRC with the
changed predicate. Something like this:
self.fetchedResultsController = nil;
[self.fetchedResultsController performFetch:&error];
[self.tableView reloadData];
This is the pattern I use for where a fetch controller needs a property:
- (void)setSelectedCategory:(id)selectedCategory{
if(selectedCategory == _selectedCategory){
return _selectedCategory
}
_selectedCategory = selectedCategory;
self.fetchedResultsController = nil;
if(self.isViewLoaded){
[self.tableView reloadData]; // but better to put this in an update views method that you can also call from viewDidLoad.
}
}
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
id selectedCategory = self.selectedCategory;
// Only need this if a category is required.
if(!selectedCategory){
return nil;
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY category_ids.category_id == %#", selectedCategory];
_fetchedResultsController = [EyeArtist fetchAllGroupedBy:nil withPredicate:predicate sortedBy:#"artist_id" ascending:NO delegate:self];
return _fetchedResultsController;
}

Setting a predicate for different tableviews

This one has been a big problem for me, and i´m still stuck with it so i was hopping that someone could give me some kind of guidance.
What i have is:
3 tableviews with multiple cells and each cell with several textfields.
1 tableview that appears inside a popover every time a specific textfield on those cells is pressed. This tableview has all!! the core data methods to retrieve the necessary data from my database.
Everything works ok...but i need to distinguish what kind of data shall appear in tableview 1 or 2 or 3...So i know i have to use predicate!.
What i have done: ( and i have tried other things)
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController == nil)
{
NSFetchRequest *fetchRequestList = [[NSFetchRequest alloc] init];
NSEntityDescription *entityList = [NSEntityDescription entityForName:#"List" inManagedObjectContext:self.managedObjectContext];
[fetchRequestLista setEntity:entityList];
TableViewOne *table1 = [[Cobertura alloc]init];
TableViewTwo *table2 = [[Cobertura alloc]init];
if (table1 textFieldShouldBeginEditing:table1.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
}
if (table2 textFieldShouldBeginEditing:table2.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview2];
}
NSSortDescriptor *cellTitle = [[NSSortDescriptor alloc] initWithKey:#"reference" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:cellTitle, nil];
[fetchRequestLista setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequestLista managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"referencia" cacheName:nil];
_fetchedResultsController.delegate = self;
self.fetchedResultsController = _fetchedResultsController;
return _fetchedResultsController;
}
In each of my tableviews, i have an instance of the "popoverTableview" in my method textFieldShouldBeginEditing:
popoverTableview = [self.storyboard instantiateViewControllerWithIdentifier:#"popoverTableview"];
popover = [[UIPopoverController alloc] initWithContentViewController:popoverTableview];
[popover presentPopoverFromRect:textField.bounds inView:textField permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverTableview.delegate = self;
popoverTableview.popView = self.popover;
So, if i´m in tableview1 i need to get [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
Should i be creating some kind of method that my tableviewS could access? What am i forgetting here or not paying attention?
Thanks in advance, and any kind of advise would be welcome!
Regards
For everyone that was experiencing the same problem that i was, here is what i have done to resolve:
This is when i´m creating the popoverview when a specific textfield is pressed:
popoverTableview = [self.storyboard instantiateViewControllerWithIdentifier:#"popoverTableview"]initWithTextFieldTag:myTextFieldThatWasPressed.tag]
popover = [[UIPopoverController alloc] initWithContentViewController:popoverTableview];
[popover presentPopoverFromRect:textField.bounds inView:textField permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverTableview.delegate = self;
popoverTableview.popView = self.popover;
popoverTableview.aIntVariable = myTextFieldThatWasPressed;
then in my popovertableview:
- (id)initWithTextFieldTag:(int)textFieldTag
{
self.aIntVariable = textFieldTag;
return self;
}
Then in the fetchedResultsController method, you´ll just have to create simple if´s telling wich predicate you want...
Regards
If the fetched results controller is for the popover table and you need to know in which table the text field was selected, I'd recommend tagging each of the text fields when you create them and creating an int _currentTable instance variable. That way, when your textFieldShouldBeginEditing: method is called, you can set the ivar's value with the tag and check that tag when creating the fetched results controller for the popover table.
So, instead of
if (table1 textFieldShouldBeginEditing:table1.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
}
if (table2 textFieldShouldBeginEditing:table2.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview2];
}
you'll have
if (_currentTable == 1) {
fetchRequestList.predicate = // table one predicate
} else if (_currentTable == 2) {
fetchRequestList.predicate = // table two predicate
}
UPDATE:
This is how I would override the init from code. In your popover table view controller implementation:
- (id)initWithTableTag:(int)tableTag
{
self = [super init];
if (self) {
_currentTable = tableTag;
}
return self;
}
(Make sure you also declare - (id)initWithTableTag:(int)tableTag; in your header.) Then, when you create and present the popover controller (which I'm assuming you're doing in the textFieldShouldBeginEditing: delegate call):
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// ...
YourPopoverTableViewControllerClass *vc = [[YourPopoverTableViewControllerClass alloc] initWithTableTag:textField.tag];
// ...
// display the popover
return YES;
}
Unfortunately, I don't know how to do this using storyboards.

Find assets in library - add to a AVMutableComposition - export = crash

I've been struggling with adding assets from the iPhone Photo Library to a AVMutableComposition and then export them. Here is what I got:
Finding the assets: (here I grab the AVURLAsset)
-(void) findAssets {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just videos.
[group setAssetsFilter:[ALAssetsFilter allVideos]];
[group enumerateAssetsUsingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop){
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
NSURL *url = [representation url];
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
// Do something interesting with the AV asset.
[thumbs addObject:alAsset];
[assets addObject:avAsset];
}else if(alAsset == nil){
[self createScroll];
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(#"No groups");
}];
[library release];
}
Here I add a asset to my composition (I use the first object in the array for testing only.
-(void) addToCompositionWithAsset:(AVURLAsset*)_asset{
NSError *editError = nil;
composition = [AVMutableComposition composition];
AVURLAsset* sourceAsset = [assets objectAtIndex:0];
Float64 inSeconds = 1.0;
Float64 outSeconds = 2.0;
// calculate time
CMTime inTime = CMTimeMakeWithSeconds(inSeconds, 600);
CMTime outTime = CMTimeMakeWithSeconds(outSeconds, 600);
CMTime duration = CMTimeSubtract(outTime, inTime);
CMTimeRange editRange = CMTimeRangeMake(inTime, duration);
[composition insertTimeRange:editRange ofAsset:sourceAsset atTime:composition.duration error:&editError];
if (!editError) {
CMTimeGetSeconds (composition.duration);
}
}
And finally I export the comp and here it crashes
-(void)exportComposition {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
NSLog (#"can export: %#", exportSession.supportedFileTypes);
NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [dirs objectAtIndex:0];
NSString *exportPath = [documentsDirectoryPath stringByAppendingPathComponent:EXPORT_NAME];
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
NSURL *exportURL = [NSURL fileURLWithPath:exportPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;//#"com.apple.quicktime-movie";
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog (#"i is in your block, exportin. status is %d",
exportSession.status);
switch (exportSession.status) {
case AVAssetExportSessionStatusFailed:
case AVAssetExportSessionStatusCompleted: {
[self performSelectorOnMainThread:#selector (exportDone:)
withObject:nil
waitUntilDone:NO];
break;
}
};
}];
}
Does anyone have an idea of what it might be? It crashes on
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
And I tried different presets and outputFileTypes.
Thanks
* SOLVED *
I have to answer my own question now when I have solved. It's amazing that I've been struggling with this for a whole day and then I fix it right after posting a question :)
I changed and moved:
composition = [AVMutableComposition
composition];
to:
composition = [[AVMutableComposition
alloc] init];
I think I was too tired when I was working on this yesterday. Thanks guys!

Mapkit Annotation type when zooming in and out?

i am working with Mapkit and i am on SDK 4.2, i am having a strange bug here, in fact i have 3 annotation types, "blue.png", red.png,black.png. I am loading these by a flux and depending on the type its will select these annotation types. Everything works fine when the map is loaded i have the the different annotation view, but when i move , zoom in or zoom out the annotation view changes i.e where it was supposed to be blue.png it becomes black.png.
I am actually testing it on device.
Thank you very much :)
Hey veer the problem is that this method is called if the user pans the map to view another location and then comes back to the place where the annotations are plotted.
- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation
I have seen many sample code for map application and this in what most of the people are using.
- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if(annotationView)
return annotationView;
else
{
MKPinAnnotationView* pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.draggable = YES;
pinView.pinColor = MKPinAnnotationColorGreen;
return pinView;
}
return nil;
}
i found the solution - in fact i am using a custom annotation view and having 3 diff types of images :
Soln:
- (AnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
AnnotationView *annotationView = nil;
// determine the type of annotation, and produce the correct type of annotation view for it.
AnnotationDetails* myAnnotation = (AnnotationDetails *)annotation;
if(myAnnotation.annotationType == AnnotationTypeGeo)
{
// annotation for your current position
NSString* identifier = #"geo";
AnnotationView *newAnnotationView = (AnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == newAnnotationView)
{
newAnnotationView = [[[AnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease];
}
annotationView = newAnnotationView;
}
else if(myAnnotation.annotationType == AnnotationTypeMyfriends)
{
NSString* identifier = #"friends";
AnnotationView *newAnnotationView = (AnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == newAnnotationView)
{
newAnnotationView = [[[AnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease];
}
annotationView = newAnnotationView;
}
}

How to capture Tap gesture on MKMapView

I am trying to capture tap event on my MKMapView, this way I can drop a MKPinAnnotation on the point where user tapped. Basically I have a map overlayed with MKOverlayViews (an overlay showing a building) and I would like to give user more information about that Overlay when they tap on it by dropping a MKPinAnnotaion and showing more information in the callout.
Thank you.
You can use a UIGestureRecognizer to detect touches on the map view.
Instead of a single tap, however, I would suggest looking for a double tap (UITapGestureRecognizer) or a long press (UILongPressGestureRecognizer). A single tap might interfere with the user trying to single tap on the pin or callout itself.
In the place where you setup the map view (in viewDidLoad for example), attach the gesture recognizer to the map view:
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleGesture:)];
tgr.numberOfTapsRequired = 2;
tgr.numberOfTouchesRequired = 1;
[mapView addGestureRecognizer:tgr];
[tgr release];
or to use a long press:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleGesture:)];
lpgr.minimumPressDuration = 2.0; //user must press for 2 seconds
[mapView addGestureRecognizer:lpgr];
[lpgr release];
In the handleGesture: method:
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
return;
CGPoint touchPoint = [gestureRecognizer locationInView:mapView];
CLLocationCoordinate2D touchMapCoordinate =
[mapView convertPoint:touchPoint toCoordinateFromView:mapView];
MKPointAnnotation *pa = [[MKPointAnnotation alloc] init];
pa.coordinate = touchMapCoordinate;
pa.title = #"Hello";
[mapView addAnnotation:pa];
[pa release];
}
I setup a long press (UILongPressGestureRecognizer) in viewDidLoad: but it just detect the only one touch from the first.
Where can i setup a long press to detect all touch? (it means the map ready everytime waiting user touch to screen to push a pin)
The viewDidLoad: method!
- (void)viewDidLoad {
[super viewDidLoad];mapView.mapType = MKMapTypeStandard;
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressGesture:)];
[self.mapView addGestureRecognizer:longPressGesture];
[longPressGesture release];
mapAnnotations = [[NSMutableArray alloc] init];
MyLocation *location = [[MyLocation alloc] init];
[mapAnnotations addObject:location];
[self gotoLocation];
[self.mapView addAnnotations:self.mapAnnotations];
}
and the handleLongPressGesture method:
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
// This is important if you only want to receive one tap and hold event
if (sender.state == UIGestureRecognizerStateEnded)
{NSLog(#"Released!");
[self.mapView removeGestureRecognizer:sender];
}
else
{
// Here we get the CGPoint for the touch and convert it to latitude and longitude coordinates to display on the map
CGPoint point = [sender locationInView:self.mapView];
CLLocationCoordinate2D locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
// Then all you have to do is create the annotation and add it to the map
MyLocation *dropPin = [[MyLocation alloc] init];
dropPin.latitude = [NSNumber numberWithDouble:locCoord.latitude];
dropPin.longitude = [NSNumber numberWithDouble:locCoord.longitude];
// [self.mapView addAnnotation:dropPin];
[mapAnnotations addObject:dropPin];
[dropPin release];
NSLog(#"Hold!!");
NSLog(#"Count: %d", [mapAnnotations count]);
}
}
If you want to use a single click/tap in the map view, here's a snippet of code I'm using. (Cocoa and Swift)
let gr = NSClickGestureRecognizer(target: self, action: "createPoint:")
gr.numberOfClicksRequired = 1
gr.delaysPrimaryMouseButtonEvents = false // allows +/- button press
gr.delegate = self
map.addGestureRecognizer(gr)
in the gesture delegate method, a simple test to prefer the double-tap gesture …
func gestureRecognizer(gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: NSGestureRecognizer) -> Bool {
let other = otherGestureRecognizer as? NSClickGestureRecognizer
if (other?.numberOfClicksRequired > 1) {
return true; // allows double click
}
return false
}
you could also filter the gesture in other delegate methods if you wanted the Map to be in various "states", one of which allowed the single tap/click
For some reason, the UIGestureRecognizer just didn't work for me in Swift. When I use the UIGestureRecognizer way. When I used the touchesEnded method, it returns a MKNewAnnotationContainerView. It seems that this MKNewAnnotationContainerView blocked my MKMapView. Fortunately enough, it's a subview of MKMapView. So I looped through MKNewAnnotationContainerView's superviews till self.view to get the MKMapView. And I managed to pin the mapView by tapping.
Swift 4.1
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let t = touches.first
print(t?.location(in: self.view) as Any)
print(t?.view?.superview?.superview.self as Any)
print(mapView.self as Any)
var tempView = t?.view
while tempView != self.view {
if tempView != mapView {
tempView = tempView?.superview!
}else if tempView == mapView{
break
}
}
let convertedCoor = mapView.convert((t?.location(in: mapView))!, toCoordinateFrom: mapView)
let pin = MKPointAnnotation()
pin.coordinate = convertedCoor
mapView.addAnnotation(pin)
}

Resources