How to search for locations using UISearchBar with autocompletion and suggestions? - ios

I am developing an app in which user can search for a point of interests, pick a search result and then the MKMapView will be centered to the result coordinate.
My question is how to make autocompletion happen? I have did research on MKLocalSearch and MKLocalSearchRequest, and it seems that is Apple suggested API for location search on iOS6.1+. However I cannot find any examples with autocompletion or suggestions with MKLocalSearch and MKLocalSearchRequest. Is it possible to autocomplete a location search or display a list of suggestions just like Apple's Maps app? Thanks!

Check this post: https://stackoverflow.com/a/20141677/1464327
Basically, you can make multiple requests. For exemple, when the user types, start a timer, when the timer finishes, make a request. Whenever the user types, cancel the previous timer.
Implement textField:shouldChangeCharactersInRange:replacementString: of the text field delegate.
static NSTimer *_timer = nil;
[_timer invalidate];
_timer = [NSTimer timerWithTimeInterval:1.5 target:self selector:#selector(_search:) userInfo:nil repeats:NO];
Then implement the _search method to make the request.
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.region = regionToSearchIn;
request.naturalLanguageQuery = self.textField.text;
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
// check for error and process the response
}];
I've never implemented something like this. I'm just telling what my starting point would be. Hopefully this will give you some direction.

Related

How to support multiple transport types with MKDirectionsRequest

MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
request.source = source;
request.destination = destination;
request.transportType = MKDirectionsTransportTypeAny;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateETAWithCompletionHandler:^(MKETAResponse * _Nullable response, NSError * _Nullable error) {
...
}];
First of all, response.transportType here is MKDirectionsTransportTypeWalking. Why does it choose that?
Second, what I really want is travel time for all 3 transport types, Transit, Walking, and Automobile. What is the best way to get all three? It seems wasteful to create 3 MKDirectionsRequest objects and run this code 3 times with different transport types. Surely Apple anticipated that we would need a way to get all 3 at once right?
To answer your first question -
If you go to Maps Settings in Settings preferred transport type might have set to walking thats why you are getting walking directions. I believe it will override if walking time is more than some threshold.
For second try passing multiple options with '|' eg. MKDirectionsTransportTypeWalking | MKDirectionsTransportTypeDriving. Not sure if it will work or not but worth trying. One more thing to note MKDirectionsTransportTypeTransit is only supported for ETA and does not return directions.

MKLocalSearch not working like Apple Maps search?

I have created a MapKit and trying to play around with MKLocalSearch. One thing I noticed in comparison to Apple Maps, is that mklocalsearch is restricted to 10 results. So how does Apple Maps display 15 suggestions under the search bar?
Okay, on to an example. Im trying to find "Barcelona." In Apple Maps it will be suggested after writing just "barc" and it will stay on the suggestion list throughout typing barcelona.
Now in my own Map view, I actually have to type in the full Barcelona to get the suggestion: Spain, Barcelona. On my way I get other suggestions, but nothing like Spain, Barcelona and not like Apple maps.
Any insight on how to get it working and to why Apple Maps work differently (spec. the 15 results vs 10 with mklocalseach)
Here is the code called on textField Changes:
- (IBAction)searchFieldChanged:(UITextField *)sender {
if(self.locationTextfield.text.length>0)
self.tableView.hidden = NO;
else
self.tableView.hidden = YES;
NSString *query = self.locationTextfield.text;
// Create and initialize a search request object.
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = query;
request.region = self.mapsView.region;//we dont want region-specific search results!
//request.region = MKCoordinateRegionMakeWithDistance(self.mapsView.userLocation.location.coordinate,40000000, 15000000);
// Create and initialize a search object.
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
// Start the search and display the results as annotations on the map.
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error)
{
[placeMarks removeAllObjects];
NSLog(#"p-count: %lu", response.mapItems.count);
for (MKMapItem *item in response.mapItems) {
[placeMarks addObject:item.placemark];
self.tempPlacemark = item.placemark;
NSLog(#"placemark: %#", item.placemark);//.location.coordinate.latitude);
}
//if(placemarks.count==0)
// appDelegate.staticPlacemark = nil;
//[self.mapsView removeAnnotations:[self.mapsView annotations]];
//[self.mapsView showAnnotations:placemarks animated:NO];
[self.tableView reloadData];
}];
}
What you do is a MKLocalSearch using a MKLocalSearchRequest. What Apple in its macOS and iOS map apps does is using the newer MKLocalSearchCompleter class to obtain autocompletion suggestions. These suggestions are used for realtime search and displayed in a UITableView. When the user selects one entry that suggestion is used to initialize a MKLocalSearchRequest to obtain detailled information about this location.

Map Location names in drop down list

My App Has to integrate search location names using text box. Is Map kit has any method to implement this kind of a functionality ? This below image displays what i exactly need
No , there is not any method of MapKit to implement this type of property..
For That You have to implement Your own Code, i.e. use of UISearchbarCantroller or Simple UISearchbar.
For Search results you can use Google Place Autocomplete api.
You can Use this GooglePlacesAutocomplete Example.
In
iOS >= 6.1 provides MKLocalSearch, MKLocalSearchRequest to search for natural language points of interest. Sample
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.region = regionToSearchIn;
request.naturalLanguageQuery = #"restaurants"; // or business name
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
// do something with the results / error
}];

Show an AlertView, do parsing and dismiss AlertView - with GCD

I m very new to iOS, as stated in the question above; im trying to do these 3 simple step.
Show Alert view
Do parsing stuff
Dismiss Alert
I was looking for something like we have in android i.e Pre Execute, doInBackground and Post Execute().
This is what i have tried.
parserAlert = [[UIAlertView alloc] initWithTitle:#"Loading" message:#"Please Wait" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[parserAlert show];
dispatch_queue_t queue = dispatch_queue_create("com.abc.testing", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue,^{
DBHandler *myDB= [[DBHandler alloc] init];
[myDB fetchResults];
dispatch_async(dispatch_get_main_queue(),^{
[parserAlert dismissWithClickedButtonIndex:0 animated:YES];
});
});
Below is the fetchResult method.
- (void) fetchResults
{
IParser *i = [[IParser alloc] init];
[i startParse];
AGParser *ag = [[AGParser alloc] init];
[ag startParse];
GParser *g = [[GParser alloc] init];
[g startParse];
HParser *h = [[HParser alloc] init];
[h startParse];
SParser *s = [[SParser alloc] init];
[s startParse];
}
This is startParse.
NSString *url = #"http://abcd.com/Service_URL/Service.asmx/GetNotes";
NSURL *nsUrl = [[NSURL alloc] initWithString:url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:nsUrl];
NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:request delegate:self];
responseData = [[NSMutableData alloc] init];
[con start];
When i run the above code, Alerview show and dismiss within a second. Adding logs on methods i observed that fetchresults method return immediately and alert view gets dismiss. However fetchResults associated threads(Connection methods, Parser methods) keep executing but alerview is dismissed.
I need a guideline how to block the code until all associated methods are finished.
Thanks for your time.
I know this is not the answer you want, but don't use an alert view for this. A nice way to cover for time-consuming activity to is to put up a UIActivityIndicatorView, or a view that contains one, and set it spinning:
http://www.apeth.com/iOSBook/ch25.html#_uiactivityindicatorview
You can also prevent user interaction while the time-consuming activity is happening, with the shared application object's beginIgnoring... (and turn that off with endIgnoring... when you're done). Obviously you can't do that, though, if the user is to be given a Cancel button. In that case, cover everything else with an invisible view (clear background color) whose userInteractionEnabled is YES, so that it eats any touches intended for anything other than the button.
Also, it is almost never the right answer to use dispatch_sync. Once you've frozen the interface in the way I've just described, you can just do your connections (asynchronous) and parsing (on a background thread) and then come back into the main thread to dismiss the activity indicator.
Finally, you're going to want to leave yourself a way out in case things go wrong. You could run an NSTimer, for example.
EDIT: And now for the actual answer to your actual question, i.e. why is my code not pausing even though I used dispatch_sync: it's because [[NSURLConnection alloc] initWithRequest:request delegate:self] returns immediately; the networking is in yet another background thread. So your startParse returns, your fetchResults returns, and meanwhile the networking continues and the NSURLConnection delegate methods are called some time later.
Here is the link what you are looking for MBProgressHUD
First alloc the MBProgressHUD instance of it in the viewDidLoad
MBProgressHUD *progressHUD = [[MBProgressHUD alloc] initWithView:self.view];
progress.delegate=self;
[progressHUD showWhileExecuting:#selector(performBackgroundTask) onTarget:self withObject:nil animated:YES]
and in the background method
-(void)performBackgroundTask
{
//Do some stuff
}
and soon as the task in the )performBackgroundTaskmethod is completed the Activity indicator shown in the MBProgressHUD will hidden and the delegate method called
-(void)hudWasHidden
{
//Do additional stuff after completion of background task
}
Hope it will help you.

How to move to the next page in Facebook JSON response using iOS SDK?

In Facebook iOS SDK, I can ask for queries like this:
[_facebook requestWithGraphPath:#"me/feed" andDelegate:self];
But often Facebook will give a limited JSON response with a URL to be used to request to move to earlier dates, for example. So in the JSON response, I'll have:
data = ( /*things here... status updates, photos, etc...*/
);
paging = {
next = "https://graph.facebook.com/me/feed?sdk=ios&sdk_version=2&access_token= <something>&until=2010-12-04";
previous = "https://graph.facebook.com/me/feed?sdk=ios&sdk_version=2&access_token=<something>&since=<something>";
};
What I'm wondering is... How do I go to the previous URL? Does the SDK provide an interface to do this?
EDIT: If possible, I actually want answer with Graph API, as Facebook is currently deprecating the REST API.
BONUS: If anyone can explain the time format that's returned by Facebook. I have 2010-09-13T00%3A25%3A16%2B0000 as an example.
all what you need that add a method to the Facebook subclass or itself
- (void)requestWithURLString:(NSString *)fullURL
andHttpMethod:(NSString *)httpMethod
andDelegate:(id <FBRequestDelegate>)delegate {
[self openUrl:fullURL params:nil httpMethod:httpMethod delegate:delegate];
}
ps the second param "httpMethod" may be always #"GET" you can omit it
With Facebook's iOS SDK version 3 out, the original answer no longer applies to the current version.
I had to do some digging, because the new version doesn't make this any easier. I found an example of how to get this done in the FBGraphObjectPagingLoader class that the new SDK provides to help do this for tables. It's incredibly ugly, but I assume that it's the "recommended" method since it's what they use.
Here's my slight modification of their code (found originally in FBGraphObjectPagingLoader's followNextLink method)
FBRequest *request = [[[FBRequest alloc] initWithSession:FBSession.activeSession graphPath:nil] autorelease];
FBRequestConnection *connection = [[[FBRequestConnection alloc] init] autorelease];
[connection addRequest:request completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
// Do some stuff
}];
// Override the URL using the one passed back in 'next'.
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
connection.urlRequest = urlRequest;
[connection start];
You could of course modify their library and encapsulate this in the class itself if you wanted to.
You can do something like this:
[appDelegate.fb requestWithGraphPath:#"me/home"
andParams:[NSMutableDictionary dictionaryWithObject:#"2011-01-27T04%3A48%3A50%2B0000" forKey:#"since"]
andDelegate:self];
Notice that, in the paging portion of your feed, the next and previous URLs differ just by one query parameter (until and since). You can use the values you grab from this to get the next and previous page of results.
Hope this helps!
Yes you can get the result by calling the function in api
I used below code to get the statuses of users in your case you can use stream.get method you can found it here http://developers.facebook.com/docs/reference/rest/stream.get/
NSMutableDictionary * params = [[NSMutableDictionary alloc] init];
[params setValue:[NSString stringWithFormat:#"%#", appDelegate.user_id] forKey:#"uid"];
[params setValue:#"150" forKey:#"limit"];
[params setValue:#"results" forKey:#"callback"];
[_facebook requestWithMethodName: #"status.get"
  andParams: params
  andHttpMethod: #"POST"
andDelegate: self];
You can use this code for you purpose.
At least as far as the date format goes, its a variant of RFC 3339 format. As far as I know, iOS doesn't have a pre-defined formatter of that type.
I create a date formatter and keep it around (they're are strangely slow to create) so I can easily convert them when working with FB data.
NSDateFormatter * sRFC3339DateFormatter = nil;
NSLocale * enUSPOSIXLocale;
sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
enUSPOSIXLocale = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease];
[sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
[sRFC3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
[sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
Once you have such a formatter, you can easily convert back and forth.
NSDate* myDate = nil;
myDate = [sRFC3339DateFormatter dateFromString:#"2011-01-27T04%3A48%3A50%2B0000"];
Breaking the URL up into a dictionary of strings is pretty straightforward with NSURL methods.

Resources