Objective-C - Search for streets based on user-entered query - objective-c

I want to allow the users to search for a street name and have the results displayed in a UITableView.
For the moment the region is not important, it can be from any region.
I could not find any relevant example in my searches and I don't know if I should use CLLocation or MKLocalSearch.
Based on docs, I should use MKLocalSearch:
Although local search and geocoding are similar, they support
different use cases. Use geocoding when you want to convert between
map coordinates and a structured address, such as an Address Book
address. Use local search when you want to find a set of locations
that match the user’s input.
But I have tried both methods and it gives me only 1 result (even-though there is an NSArray returned.
This is the CLGeocoder approach:
CLGeocoder *geocoding = [[CLGeocoder alloc] init];
[geocoding geocodeAddressString:theTextField.text completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
NSLog(#"%i", [placemarks count]);
for(CLPlacemark *myStr in placemarks) {
NSLog(#"%#", myStr);
}
}
}];
And this is my MKLocalSearch try:
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = theTextField.text;
request.region = self.region;
localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error){
if (error != nil) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Map Error",nil)
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",nil) otherButtonTitles:nil] show];
return;
}
if ([response.mapItems count] == 0) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"No Results",nil)
message:nil
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",nil) otherButtonTitles:nil] show];
return;
}
self.streets = response;
[self.streetsTableView reloadData];
}];
MKLocalSearch seems to return more than 1 response in some cases, but these are related to places not street names searches.
Thanks in advance.

This is the closest I could get. This involves using google places API Web Service.
Note: You could probably use their Google Maps API, etc. I am sure there are other ways to get this information from the various Google APIs.
NSURL *googlePlacesURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&location=%f,%f&sensor=true&key=API_KEY", formattedSearchText, location.coordinate.latitude,
location.coordinate.longitude]];
The response is a JSON object. Convert it to a dictionary.
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:_googlePlacesResponse
options:NSJSONReadingMutableContainers error:&error];
if([[response objectForKey:#"status"] isEqualToString:#"OK"])
{
NSArray *predictions = [response objectForKey:#"predictions"];
for(NSDictionary *prediction in predictions)
{
NSArray *addressTypes = [prediction objectForKey:#"types"];
if([addressTypes containsObject:#"route"])
{
//This search result contains a street name.
//Now get the street name.
NSArray *terms = [prediction objectForKey:#"terms"];
NSDictionary *streetNameKeyValuePair = [terms objectAtIndex:0];
NSLog(#"%#",[streetNameKeyValuePair objectForKey#"value"]);
}
}
}
The possible types seem to be
Route -> Street name
Locality -> City/location name
Political -> State, etc
Geocode -> lat/long available
You could populate the table view with those predictions that ONLY contain route as an address type. This could work.

CLGeocoder simply returns address format.
Slap this in your code and play with the contents of mapitems
MKLocalSearchRequest *request = [MKLocalSearchRequest new];
request.naturalLanguageQuery = #"Pizza";
request.region = MKCoordinateRegionMake(location.coordinate, MKCoordinateSpanMake(.01, .01));
MKLocalSearch *search = [[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
NSArray *mapItems = response.mapItems;
for (MKMapItem *mapItem in mapItems) {
MKPointAnnotation *point = [MKPointAnnotation new];
point.coordinate = mapItem.placemark.coordinate;`
}
}];

The array returned contains mapItems, you can iterate over the array to pull out all the mapItems like this:
myMatchingItems = [[NSMutableArray alloc] init];
for (MKMapItem *item in response.mapItems){
[myMatchingItems addObject:item];
}
Each mapItem.placemark.thoroughfare contains the Street of the location that was found.

Related

Obj-C - Google Maps SDK get data for selected marker?

I'm trying to get the data for a selected marker using the following code:
-(void)viewDidLoad {
NSMutableDictionary *viewParams3 = [NSMutableDictionary new];
[viewParams3 setValue:#"breweries" forKey:#"view_name"];
[DIOSView viewGet:viewParams3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.breweryLocations = [responseObject mutableCopy];
int index = 0;
for (NSMutableDictionary *brewInfo in self.breweryLocations) {
NSString *location = brewInfo[#"address"];
NSString *userNames = brewInfo[#"node_title"];
NSString *firstRemoved = [userNames stringByReplacingOccurrencesOfString:#"'" withString:#""];
NSString *userBio = brewInfo[#"body"];
CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
[geocoderFriend geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
CLLocationCoordinate2D position = placemark.coordinate;
GMSMarker *marker = [GMSMarker markerWithPosition:position];
marker.title = firstRemoved;
marker.icon = [UIImage imageNamed:#"brewIconMax"];
marker.map = self.mapView;
marker.userData = self.breweryLocations;
marker.map.selectedMarker.zIndex = index + 1;
}
}
];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
}
- (BOOL) mapView: (GMSMapView *)mapView didTapMarker:(GMSMarker *)marker
{
self.venueCategory.text = marker.userData[mapView.selectedMarker.zIndex][#"type"];
}
That said, this line of code
self.venueCategory.text = marker.userData[mapView.selectedMarker.zIndex][#"type"];
returns the data from the first dictionary in my array no matter which marker I tap (zIndex is always returned as 0). I can't seem to find the correct code to obtain the selected marker's array data anywhere.
Any idea what that line should look like instead?
It's not clear to me whether "type" is an element of the brewery location dictionary, so I'm taking some guesses here.
I'd suggest setting marker.userData to the brewInfo dictionary, and not the entire breweryLocations array.
marker.userData = brewInfo;
Then this to get the "type" from the brewInfo dictionary.
self.venueCategory.text = marker.userData[#"type"];

How to share HashTag Text with Video On Facebook in IOS?

I'm sharing the video on Facebook (Without the SLComposer) from my IOS App. it will send successfully but I want To add the HashTag Text With it. Im Trying it But It will not get add shared with the video (only video get shared ).
FBSDKShareVideo *ShareVideo = [FBSDKShareVideo videoWithVideoURL:appDelegateObj.finalVideoUrl];
ShareVideo.videoURL = appDelegateObj.finalVideoUrl;
FBSDKShareVideoContent *ShareContnt = [[FBSDKShareVideoContent alloc] init];
ShareContnt.video = ShareVideo;
ShareContnt.hashtag = [FBSDKHashtag hashtagWithString:[NSString stringWithFormat:#"%#",#"We are #sharing this #video for the #testing of #video and the #HashTag Text"]];
[FBSDKShareAPI shareWithContent:ShareContnt delegate:self];
Please Help me for this issues ?
100% Working
I got the ANS Of these...
//Using these code we only share can't send the text / Title or name of video...
-(void)facbookSharng
{
NSLog(#"Permission for sharing..%#",[FBSDKAccessToken currentAccessToken].permissions);
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"contact_email"])
{
FBSDKShareVideo *ShareVideo = [FBSDKShareVideo videoWithVideoURL:appDelegateObj.finalVideoUrl];
ShareVideo.videoURL = appDelegateObj.finalVideoUrl;
FBSDKShareVideoContent *ShareContnt = [[FBSDKShareVideoContent alloc] init];
ShareContnt.video = ShareVideo;
[FBSDKShareAPI shareWithContent:ShareContnt delegate:self]
// write the deleate methdo for post ID..
}
}
//But for these Facebook gives another way,
NSLog(#"Permission for sharing..%#",[FBSDKAccessToken currentAccessToken].permissions);
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"contact_email"])
{
NSData *videoData = [NSData dataWithContentsOfURL:appDelegateObj.finalVideoUrl];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3L];
[params setObject:videoData forKey:#"video_filename.MOV"];
[params setObject:#"Title for this post." forKey:#"title"];
[params setObject:#"#Description for this post." forKey:#"description"];
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/me/videos" parameters:params HTTPMethod:#"POST"]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
//video posted
NSLog(#"Facebook sharing completed %#:",result);
strFbSocialPostId = [result valueForKey:#"id"];//post ID
}
}];
}

Sending push notification while in background objective c

First of all i am using parse.com to store information.
This code simply opens the Maps app every time this method is run and saves the users location in a server.
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
[request setSource:[MKMapItem mapItemForCurrentLocation]];
[request setDestination:endingItem];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
[request setRequestsAlternateRoutes:YES];
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if ( ! error && [response routes] > 0) {
MKRoute *route = [[response routes] objectAtIndex:0];
//route.distance = The distance
NSLog(#"total %f",route.expectedTravelTime );
int time = ceil(route.expectedTravelTime/60);
self.ETA = [#(time) stringValue];
NSLog(#"test %d",time);
NSLog(#"Total Distance (in Meters) :%0.1f",route.distance/1000);
self.distance = [#(route.distance*4899) stringValue];
// IF decline was pressed, need to fix if it's accepted
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:self.distance forKey:#"dist"];
[PFCloud callFunctionInBackground:#"sendAccepted" withParameters:params block:^(id object, NSError *error) {
if (!error) {
NSLog(#"Success answer sent");
} else {
NSLog(#"Failed to push");
}
}];
}
}];
[endingItem openInMapsWithLaunchOptions:launchOptions];
}
What i noticed is that if Maps application is already open when this method is run then it does not save the users data until i return to the applikation. HOWEVER if i close the Maps application before this method is run the it is always sent to the server.
Now the problem i think is that it obviously takes more time for Maps app to open if it was not opened before hence giving my applikation more time to complete the update. How can i solve this so it will still update the location even if my applikation goes to the background?

Change starting location when opening Apple Maps from within app

By default when opening Apple Maps through a MKMapItem it uses the device's location as the starting point but i'm trying to allow a user to add their own starting point by entering in the proper fields (address, city, state, zipcode) and passing the address string to
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
NSString *addressString = [NSString stringWithFormat:#"%# %# %# %#",
self.address.text,
self.city.text,
self.state.text,
self.zip.text];
[geocoder geocodeAddressString:addressString
completionHandler:^(NSArray *placemarks, NSError *error)
{
CLLocationCoordinate2D addressCoordinates;
if (error.code == 2)
{
UIAlertView *networkConnectionAlert = [[UIAlertView alloc] initWithTitle:#"Network Error" message:#"The internet connections appears to be offline. Ensure that data is enabled or that your device is connected to a wireless connection." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[networkConnectionAlert show];
NSLog(#"Geocode failed with error: %#", error);
return;
}
if (placemarks && placemarks.count > 0)
{
CLPlacemark *placemark = placemarks[0];
// Location of address passed in
CLLocation *location = placemark.location;
// Coordinates of address passed in
addressCoordinates = location.coordinate;
}
// Destination address dictionary values
NSDictionary *address = #{
(NSString *)kABPersonAddressStreetKey: self.address.text,
(NSString *)kABPersonAddressCityKey: self.city.text,
(NSString *)kABPersonAddressStateKey: self.state.text,
(NSString *)kABPersonAddressZIPKey: self.zip.text
};
MKPlacemark *place = [[MKPlacemark alloc] initWithCoordinate:addressCoordinates
addressDictionary:address];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:place];
NSDictionary *options = #{MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};
[mapItem openInMapsWithLaunchOptions:options];
}];
Of course every time I run the app it uses the device's location as the starting point and the destination point as the address entered which is understandable given the code above but is there not a property that can be called to change the starting point? I know that you can change the starting address/location when you open the actual Apple Maps so I assume that there has to be a way and that I'm just doing something wrong or missing something. Any help on this would be greatly appreciated, thanks.

Issue forward-geocoding multiple addresses

I am connecting to a remote web service which basically returns an XML back. I am then parsing that XML into a Property object (think real state sort of thing)
But now, the web service returns a postal code for each property alone. It does not provide a coordinate which is what I need to place an annotation in the map. I am able to geocode an address provided a postal code. However, my problem is it is not allowing me to do multiple requests
Here's my code
- (void)processProperties:(Property *)property {
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
}];
}
- (void)addAnnotations:(NSArray *)objects {
CLLocationDegrees lat;
CLLocationDegrees longitude;
CLLocationCoordinate2D mCoords;
NSString *fullAddress;
// Add the annotations found nearby
for (Property *property in objects) {
[self processProperties:property];
lat = property.latitude;
longitude = property.longitude;
fullAddress = [NSString stringWithFormat:#"%# %# %#", property.houseNumber, #" ", property.streetName];
[self createAnnotationWithCoords:mCoords :fullAddress :[NSString stringWithFormat:#"$%.2f", property.rent]];
}
zoomLevel = 0.1;
mCoords = CLLocationCoordinate2DMake(lat,longitude);
MKCoordinateRegion region = MKCoordinateRegionMake(mCoords,MKCoordinateSpanMake(zoomLevel,zoomLevel));
[self.mapView setRegion:region animated:YES];
}
For some reason it's just geocoding 1 property. Is not going through the loop accordingly.
Any ideas folks?
Use this on your Forward Geo Function. geocoder needs to be release and initialized again to start a new address, hope this helps.
- (void)processProperties:(Property *)property {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
[geocoder release];
}];
}