Related
I want to draw routes on a map corresponding to directions JSON which I am getting through the Google Directions API: https://developers.google.com/maps/documentation/directions/start
I have figured out how to extract the latitude and longitude from the steps field, however this doesn't follow curvy roads very well. I think what I need is to decode the polyline information, I found Googles instructions on how to encode polylines: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
I did find some code here for Android and also Javascript on decoding the polylines, for example:
Map View draw directions using google Directions API - decoding polylines
android get and parse Google Directions
But I can't find same for Objective-C iPhone code, can anybody help me with this? I'm sure I can do it myself if I have to, but it sure would save me some time if it's already available somewhere.
EDIT: the key here is being able to decode the base64 encoding on a character by character basis. To be more specific, I get something like this in JSON from Google which is encoded using base64 encoding among other things:
... "overview_polyline" : {
"points" : "ydelDz~vpN_#NO#QEKWIYIIO?YCS#WFGBEBICCAE?G#y#RKBEBEBAD?HTpB#LALALCNEJEFSP_#LyDv#aB\\GBMB"
},
...
Note: I should mention that this question refers to Google Maps API v1, it is much easier to do this in v2 using GMSPolyLine polyLineWithPath as many answers below will tell you (thanks to #cdescours).
I hope it's not against the rules to link to my own blog post if it's relevant to the question, but I've solved this problem in the past. Stand-alone answer from linked post:
#implementation MKPolyline (MKPolyline_EncodedString)
+ (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString {
const char *bytes = [encodedString UTF8String];
NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger idx = 0;
NSUInteger count = length / 4;
CLLocationCoordinate2D *coords = calloc(count, sizeof(CLLocationCoordinate2D));
NSUInteger coordIdx = 0;
float latitude = 0;
float longitude = 0;
while (idx < length) {
char byte = 0;
int res = 0;
char shift = 0;
do {
byte = bytes[idx++] - 63;
res |= (byte & 0x1F) << shift;
shift += 5;
} while (byte >= 0x20);
float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1));
latitude += deltaLat;
shift = 0;
res = 0;
do {
byte = bytes[idx++] - 0x3F;
res |= (byte & 0x1F) << shift;
shift += 5;
} while (byte >= 0x20);
float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1));
longitude += deltaLon;
float finalLat = latitude * 1E-5;
float finalLon = longitude * 1E-5;
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(finalLat, finalLon);
coords[coordIdx++] = coord;
if (coordIdx == count) {
NSUInteger newCount = count + 10;
coords = realloc(coords, newCount * sizeof(CLLocationCoordinate2D));
count = newCount;
}
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordIdx];
free(coords);
return polyline;
}
#end
The best and lightest answer should be to use the method provided by Google in the framework :
[GMSPolyline polylineWithPath:[GMSPath pathFromEncodedPath:encodedPath]]
If you are working with Google Map on iOS and want to draw the route including the polylines, google itself provides an easier way to get the GMSPath from polyline as,
GMSPath *pathFromPolyline = [GMSPath pathFromEncodedPath:polyLinePoints];
Here is the complete code:
+ (void)callGoogleServiceToGetRouteDataFromSource:(CLLocation *)sourceLocation toDestination:(CLLocation *)destinationLocation onMap:(GMSMapView *)mapView_{
NSString *baseUrl = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false", sourceLocation.coordinate.latitude, sourceLocation.coordinate.longitude, destinationLocation.coordinate.latitude, destinationLocation.coordinate.longitude];
NSURL *url = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"Url: %#", url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
GMSMutablePath *path = [GMSMutablePath path];
NSError *error = nil;
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *routes = [result objectForKey:#"routes"];
NSDictionary *firstRoute = [routes objectAtIndex:0];
NSDictionary *leg = [[firstRoute objectForKey:#"legs"] objectAtIndex:0];
NSArray *steps = [leg objectForKey:#"steps"];
int stepIndex = 0;
CLLocationCoordinate2D stepCoordinates[1 + [steps count] + 1];
for (NSDictionary *step in steps) {
NSDictionary *start_location = [step objectForKey:#"start_location"];
stepCoordinates[++stepIndex] = [self coordinateWithLocation:start_location];
[path addCoordinate:[self coordinateWithLocation:start_location]];
NSString *polyLinePoints = [[step objectForKey:#"polyline"] objectForKey:#"points"];
GMSPath *polyLinePath = [GMSPath pathFromEncodedPath:polyLinePoints];
for (int p=0; p<polyLinePath.count; p++) {
[path addCoordinate:[polyLinePath coordinateAtIndex:p]];
}
if ([steps count] == stepIndex){
NSDictionary *end_location = [step objectForKey:#"end_location"];
stepCoordinates[++stepIndex] = [self coordinateWithLocation:end_location];
[path addCoordinate:[self coordinateWithLocation:end_location]];
}
}
GMSPolyline *polyline = nil;
polyline = [GMSPolyline polylineWithPath:path];
polyline.strokeColor = [UIColor grayColor];
polyline.strokeWidth = 3.f;
polyline.map = mapView_;
}];
}
+ (CLLocationCoordinate2D)coordinateWithLocation:(NSDictionary*)location
{
double latitude = [[location objectForKey:#"lat"] doubleValue];
double longitude = [[location objectForKey:#"lng"] doubleValue];
return CLLocationCoordinate2DMake(latitude, longitude);
}
Swift 3.0
let polyline = GMSPolyline(path: GMSPath.init(fromEncodedPath: encodedPolyline))
Python Implementation
This isn't in Objective-C, but this thread is where Google drops you if you're looking to decode polyline strings from Google Maps. In case anyone else needs it (much like I did), here's a Python implementation for decoding polyline strings. This is ported from the Mapbox JavaScript version; more info found on my repo page.
def decode_polyline(polyline_str):
index, lat, lng = 0, 0, 0
coordinates = []
changes = {'latitude': 0, 'longitude': 0}
# Coordinates have variable length when encoded, so just keep
# track of whether we've hit the end of the string. In each
# while loop iteration, a single coordinate is decoded.
while index < len(polyline_str):
# Gather lat/lon changes, store them in a dictionary to apply them later
for unit in ['latitude', 'longitude']:
shift, result = 0, 0
while True:
byte = ord(polyline_str[index]) - 63
index+=1
result |= (byte & 0x1f) << shift
shift += 5
if not byte >= 0x20:
break
if (result & 1):
changes[unit] = ~(result >> 1)
else:
changes[unit] = (result >> 1)
lat += changes['latitude']
lng += changes['longitude']
coordinates.append((lat / 100000.0, lng / 100000.0))
return coordinates
- (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString {
const char *bytes = [encodedString UTF8String];
NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger idx = 0;
NSUInteger count = length / 4;
CLLocationCoordinate2D *coords = calloc(count, sizeof(CLLocationCoordinate2D));
NSUInteger coordIdx = 0;
float latitude = 0;
float longitude = 0;
while (idx < length) {
char byte = 0;
int res = 0;
char shift = 0;
do {
byte = bytes[idx++] - 63;
res |= (byte & 0x1F) << shift;
shift += 5;
} while (byte >= 0x20);
float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1));
latitude += deltaLat;
shift = 0;
res = 0;
do {
byte = bytes[idx++] - 0x3F;
res |= (byte & 0x1F) << shift;
shift += 5;
} while (byte >= 0x20);
float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1));
longitude += deltaLon;
float finalLat = latitude * 1E-5;
float finalLon = longitude * 1E-5;
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(finalLat, finalLon);
coords[coordIdx++] = coord;
if (coordIdx == count) {
NSUInteger newCount = count + 10;
coords = realloc(coords, newCount * sizeof(CLLocationCoordinate2D));
count = newCount;
}
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordIdx];
free(coords);
return polyline;
}
- (MKPolygonRenderer *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
// MKPolygonRenderer *polylineView = [[MKPolygonRenderer alloc] initWithOverlay:overlay];
MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:overlay];
polylineView.strokeColor = [UIColor redColor];
polylineView.lineWidth = 4.0;
[self zoomToPolyLine:mapview polyline:overlay animated:YES];
return polylineView;
}
-(void)zoomToPolyLine: (MKMapView*)map polyline: (MKPolyline*)polyline animated: (BOOL)animated
{
[map setVisibleMapRect:[polyline boundingMapRect] edgePadding:UIEdgeInsetsMake(25.0, 25.0, 25.0, 25.0) animated:animated];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
currlong = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
currlt = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
NSString *origin = [NSString stringWithFormat:#"%#%#%#",currlt,#",",currlong];
//I have just mention static location
NSString *drivein = #"23.0472963,72.52757040000006";
NSString *apikey = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/directions/json?origin=%#&destination=%#",origin,drivein];
NSURL *url = [NSURL URLWithString:apikey];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if(!error)
{
NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSArray *routesArray = [jsonResponse objectForKey:#"routes"];
NSLog(#"route array %#",routesArray);
if ([routesArray count] > 0)
{
NSDictionary *routeDict = [routesArray objectAtIndex:0];
NSDictionary *routeOverviewPolyline = [routeDict objectForKey:#"overview_polyline"];
NSString *points = [routeOverviewPolyline objectForKey:#"points"];
MKPolyline *line = [self polylineWithEncodedString:points];
[mapview addOverlay:line];
}
}
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate, 500, 500);
MKCoordinateRegion adjustedRegion = [mapview regionThatFits:viewRegion];
[mapview setRegion:adjustedRegion animated:YES];
mapview.showsUserLocation = YES;
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = currentLocation.coordinate;
point.title = #"Your current Locations";
point.subtitle = #"You are here!";
[mapview addAnnotation:point];
[locationmanger stopUpdatingLocation];
}
Here's how I do it in my directions app. keyPlace is your destination object
- (void)getDirections {
CLLocation *newLocation;// = currentUserLocation;
MKPointAnnotation *annotation = [[[MKPointAnnotation alloc] init] autorelease];
annotation.coordinate = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude);
annotation.title = #"You";
[mapView addAnnotation:annotation];
CLLocationCoordinate2D endCoordinate;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false&mode=walking", newLocation.coordinate.latitude, newLocation.coordinate.longitude, keyPlace.lat, keyPlace.lon]];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
if ([[request.responseString.JSONValue valueForKey:#"status"] isEqualToString:#"ZERO_RESULTS"]) {
[[[[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not route path from your current location"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil, nil] autorelease] show];
self.navigationController.navigationBar.userInteractionEnabled = YES;
return;
}
int points_count = 0;
if ([[request.responseString.JSONValue objectForKey:#"routes"] count])
points_count = [[[[[[request.responseString.JSONValue objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] objectAtIndex:0] objectForKey:#"steps"] count];
if (!points_count) {
[[[[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not route path from your current location"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil, nil] autorelease] show];
self.navigationController.navigationBar.userInteractionEnabled = YES;
return;
}
CLLocationCoordinate2D points[points_count * 2];
int j = 0;
NSArray *steps = nil;
if (points_count && [[[[request.responseString.JSONValue objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] count])
steps = [[[[[request.responseString.JSONValue objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] objectAtIndex:0] objectForKey:#"steps"];
for (int i = 0; i < points_count; i++) {
double st_lat = [[[[steps objectAtIndex:i] objectForKey:#"start_location"] valueForKey:#"lat"] doubleValue];
double st_lon = [[[[steps objectAtIndex:i] objectForKey:#"start_location"] valueForKey:#"lng"] doubleValue];
//NSLog(#"lat lon: %f %f", st_lat, st_lon);
if (st_lat > 0.0f && st_lon > 0.0f) {
points[j] = CLLocationCoordinate2DMake(st_lat, st_lon);
j++;
}
double end_lat = [[[[steps objectAtIndex:i] objectForKey:#"end_location"] valueForKey:#"lat"] doubleValue];
double end_lon = [[[[steps objectAtIndex:i] objectForKey:#"end_location"] valueForKey:#"lng"] doubleValue];
if (end_lat > 0.0f && end_lon > 0.0f) {
points[j] = CLLocationCoordinate2DMake(end_lat, end_lon);
endCoordinate = CLLocationCoordinate2DMake(end_lat, end_lon);
j++;
}
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:points count:points_count * 2];
[mapView addOverlay:polyline];
}
#pragma mark - MapKit
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *annView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"] autorelease];
annView.canShowCallout = YES;
annView.animatesDrop = YES;
return annView;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView
viewForOverlay:(id<MKOverlay>)overlay {
MKPolylineView *overlayView = [[[MKPolylineView alloc] initWithOverlay:overlay] autorelease];
overlayView.lineWidth = 5;
overlayView.strokeColor = [UIColor purpleColor];
overlayView.fillColor = [[UIColor purpleColor] colorWithAlphaComponent:0.5f];
return overlayView;
}
In case anyone would need the decoding-code in VBA, here is a (working) port:
Function decodeGeopoints(encoded)
decodeGeopoints = ""
' This code is a port to VBA from code published here:
' http://blog.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/
'//decoding
'List poly = new ArrayList();
'// replace two backslashes by one (some error from the transmission)
'encoded = encoded.replace("\\", "\");
encoded = Replace(encoded, "\\", "\")
'int index = 0, len = encoded.length();
Dim index As Long
index = 0
Dim leng As Long
leng = Len(encoded)
'int lat = 0, lng = 0;
Dim lat As Long
lat = 0
Dim lng As Long
lng = 0
'while (index < len) {
While (index < leng)
'int b, shift = 0, result = 0;
Dim b, shift, result As Long
b = 0
shift = 0
result = 0
'do {
Do
'b = encoded.charAt(index++) - 63;
index = index + 1
b = Asc(Mid(encoded, index, 1)) - 63
'result |= (b & 0x1f) << shift;
result = result Or ((b And 31) * (2 ^ shift))
'shift += 5;
shift = shift + 5
'} while (b >= 0x20);
Loop While (b >= 32)
'int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
Dim dlat As Long
If (result And 1) <> 0 Then
dlat = Not Int(result / 2)
Else
dlat = Int(result / 2)
End If
'lat += dlat;
lat = lat + dlat
'shift = 0;
shift = 0
'result = 0;
result = 0
'do {
Do
'b = encoded.charAt(index++) - 63;
index = index + 1
b = Asc(Mid(encoded, index, 1)) - 63
'result |= (b & 0x1f) << shift;
result = result Or ((b And 31) * (2 ^ shift))
'shift += 5;
shift = shift + 5
'} while (b >= 0x20);
Loop While (b >= 32)
'int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
Dim dlng As Long
If (result And 1) <> 0 Then
dlng = Not Int(result / 2)
Else
dlng = Int(result / 2)
End If
'lng += dlng;
lng = lng + dlng
'GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));
Dim myLat, myLng As Double
myLat = (lat / 100000)
'myLat = myLat * 1000000
myLng = (lng / 100000)
'myLng = myLng * 1000000
'poly.add(p);
decodeGeopoints = decodeGeopoints & Comma2Dot(myLng) & "," & Comma2Dot(myLat) & ",0 "
'}
Wend
End Function
For Google maps it already have a straight forward method , polylineWithPath, so I prefer this snippet.
-(void)drawPathFrom:(CLLocation*)source toDestination:(CLLocation*)destination{
NSString *baseUrl = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=true", source.coordinate.latitude, source.coordinate.longitude, destination.coordinate.latitude, destination.coordinate.longitude];
NSURL *url = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"Url: %#", url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if(!connectionError){
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *routes = [result objectForKey:#"routes"];
NSDictionary *firstRoute = [routes objectAtIndex:0];
NSString *encodedPath = [firstRoute[#"overview_polyline"] objectForKey:#"points"];
GMSPolyline *polyPath = [GMSPolyline polylineWithPath:[GMSPath pathFromEncodedPath:encodedPath]];
polyPath.strokeColor = [UIColor redColor];
polyPath.strokeWidth = 3.5f;
polyPath.map = _mapView;
}
}];
}
Swift 4.2 / Swift 5
let gmsPolyline = GMSPolyline(path: GMSPath(fromEncodedPath: encodedPolyline))
gmsPolyline.map = map
This is my own revisitation of Sedate Alien's answer.
It is the same implementation save for removing duplicated code and using NSMutableData instead of manually allocating stuff.
#implementation MKPolyline (EncodedString)
+ (float)decodeBytes:(const char *)bytes atPos:(NSUInteger *)idx toValue:(float *)value {
char byte = 0;
int res = 0;
char shift = 0;
do {
byte = bytes[(*idx)++] - 0x3F;
res |= (byte & 0x1F) << shift;
shift += 5;
}
while (byte >= 0x20);
(*value) += ((res & 1) ? ~(res >> 1) : (res >> 1));
return (*value) * 1E-5;
}
+ (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString {
const char *bytes = [encodedString UTF8String];
NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger idx = 0;
NSMutableData *data = [NSMutableData data];
float lat = 0;
float lon = 0;
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(0, 0);
while (idx < length) {
coords.latitude = [self decodeBytes:bytes atPos:&idx toValue:&lat];
coords.longitude = [self decodeBytes:bytes atPos:&idx toValue:&lon];
[data appendBytes:&coords length:sizeof(CLLocationCoordinate2D)];
}
return [MKPolyline polylineWithCoordinates:(CLLocationCoordinate2D *)data.bytes count:data.length / sizeof(CLLocationCoordinate2D)];
}
#end
The other answers here seem to be about using Apple Maps, for using Google Maps I found I had to make some modifications to #SedateAlien's great category.
MODIFIED CATEGORY
+ (GMSPolyline *)polylineWithEncodedString:(NSString *)encodedString {
const char *bytes = [encodedString UTF8String];
NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger idx = 0;
NSUInteger count = length / 4;
CLLocationCoordinate2D *coords = calloc(count, sizeof(CLLocationCoordinate2D));
NSUInteger coordIdx = 0;
float latitude = 0;
float longitude = 0;
while (idx < length) {
char byte = 0;
int res = 0;
char shift = 0;
do {
byte = bytes[idx++] - 63;
res |= (byte & 0x1F) << shift;
shift += 5;
} while (byte >= 0x20);
float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1));
latitude += deltaLat;
shift = 0;
res = 0;
do {
byte = bytes[idx++] - 0x3F;
res |= (byte & 0x1F) << shift;
shift += 5;
} while (byte >= 0x20);
float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1));
longitude += deltaLon;
float finalLat = latitude * 1E-5;
float finalLon = longitude * 1E-5;
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(finalLat, finalLon);
coords[coordIdx++] = coord;
if (coordIdx == count) {
NSUInteger newCount = count + 10;
coords = realloc(coords, newCount * sizeof(CLLocationCoordinate2D));
count = newCount;
}
}
GMSMutablePath *path = [[GMSMutablePath alloc] init];
int i;
for (i = 0; i < coordIdx; i++)
{
[path addCoordinate:coords[i]];
}
GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
free(coords);
return polyline;
}
USAGE
// Here I make the call to the Google Maps API to get the routes between two points...
....
// Get the encoded array of points.
NSString *points = routes[#"routes"][0][#"overview_polyline"][#"points"];
// Use the modified category to get a polyline from the points.
GMSPolyline *polyline = [GMSPolyline polylineWithEncodedString:points];
// Add the polyline to the map.
polyline.strokeColor = [UIColor redColor];
polyline.strokeWidth = 10.f;
polyline.map = theMapView;
}
If anybody else is trying to do this in swift, here's #RootCode's answer adapted to swift (2.3):
let path = GMSMutablePath()
let steps = directionsToShowOnMap.steps
for (idx, step) in steps.enumerate() {
path.addCoordinate(coordinateFromJson(step["start_location"]))
if let polylinePoints = step["polyline"].string, subpath = GMSPath(fromEncodedPath: polylinePoints) {
for c in 0 ..< subpath.count() {
path.addCoordinate(subpath.coordinateAtIndex(c))
}
}
if idx == steps.count - 1 {
path.addCoordinate(coordinateFromJson(step["end_location"]))
}
}
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.blueColor()
polyline.strokeWidth = 3
polyline.map = mapView
and then:
private func coordinateFromJson(location: JSON) -> CLLocationCoordinate2D {
return CLLocationCoordinate2DMake(location["lat"].double!, location["lng"].double!)
}
Hi here in this code i make the image to blur. but the problem is, it is very slow on iPhone.
here is my code.
- (UIImage*) gaussianBlur:(NSUInteger)radius
{
// Pre-calculated kernel
double dKernel[5][5]={
{1.0f/273.0f, 4.0f/273.0f, 7.0f/273.0f, 4.0f/273.0f, 1.0f/273.0f},
{4.0f/273.0f, 16.0f/273.0f, 26.0f/273.0f, 16.0f/273.0f, 4.0f/273.0f},
{7.0f/273.0f, 26.0f/273.0f, 41.0f/273.0f, 26.0f/273.0f, 7.0f/273.0f},
{4.0f/273.0f, 16.0f/273.0f, 26.0f/273.0f, 16.0f/273.0f, 4.0f/273.0f},
{1.0f/273.0f, 4.0f/273.0f, 7.0f/273.0f, 4.0f/273.0f, 1.0f/273.0f}};
NSMutableArray *kernel = [[[NSMutableArray alloc] initWithCapacity:5] autorelease];
for (int i = 0; i < 5; i++) {
NSMutableArray *row = [[[NSMutableArray alloc] initWithCapacity:5] autorelease];
for (int j = 0; j < 5; j++) {
[row addObject:[NSNumber numberWithDouble:dKernel[i][j]]];
}
[kernel addObject:row];
}
return [self applyConvolve:kernel];
}
- (UIImage*) applyConvolve:(NSArray*)kernel
{
CGImageRef inImage = self.CGImage;
CFDataRef m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
CFDataRef m_OutDataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef);
UInt8 * m_OutPixelBuf = (UInt8 *) CFDataGetBytePtr(m_OutDataRef);
int h = CGImageGetHeight(inImage);
int w = CGImageGetWidth(inImage);
int kh = [kernel count] / 2;
int kw = [[kernel objectAtIndex:0] count] / 2;
int i = 0, j = 0, n = 0, m = 0;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
int outIndex = (i*w*4) + (j*4);
double r = 0, g = 0, b = 0;
for (n = -kh; n <= kh; n++) {
for (m = -kw; m <= kw; m++) {
if (i + n >= 0 && i + n < h) {
if (j + m >= 0 && j + m < w) {
double f = [[[kernel objectAtIndex:(n + kh)] objectAtIndex:(m + kw)] doubleValue];
if (f == 0) {continue;}
int inIndex = ((i+n)*w*4) + ((j+m)*4);
r += m_PixelBuf[inIndex] * f;
g += m_PixelBuf[inIndex + 1] * f;
b += m_PixelBuf[inIndex + 2] * f;
}
}
}
}
m_OutPixelBuf[outIndex] = SAFECOLOR((int)r);
m_OutPixelBuf[outIndex + 1] = SAFECOLOR((int)g);
m_OutPixelBuf[outIndex + 2] = SAFECOLOR((int)b);
m_OutPixelBuf[outIndex + 3] = 255;
}
}
CGContextRef ctx = CGBitmapContextCreate(m_OutPixelBuf,
CGImageGetWidth(inImage),
CGImageGetHeight(inImage),
CGImageGetBitsPerComponent(inImage),
CGImageGetBytesPerRow(inImage),
CGImageGetColorSpace(inImage),
CGImageGetBitmapInfo(inImage)
);
CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
CGContextRelease(ctx);
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CFRelease(m_DataRef);
CFRelease(m_OutDataRef);
return finalImage;
}
A 2-d Gaussian convolution is separable, which means it can be done as 2 1-d convolutions, one horizontal and one vertical. That should save you a bit of time.
http://en.wikipedia.org/wiki/Gaussian_blur#Implementation
I have a piece of code in vb. I need to convert array of bytes to base 64 string. Following is the vb code.
If arrLicence.Count > 0 Then
LicenceBytes = CType(Array.CreateInstance(GetType(Byte),6), Byte())
LicenceBytes(0) = Convert.ToByte(arrLicence(0).ToString(), 16)
LicenceBytes(1) = Convert.ToByte(arrLicence(1).ToString(), 16)
LicenceBytes(2) = Convert.ToByte(arrLicence(2).ToString(), 16)
LicenceBytes(3) = Convert.ToByte(arrLicence(3).ToString(), 16)
LicenceBytes(4) = Convert.ToByte(arrLicence(4).ToString(), 16)
LicenceBytes(5) = Convert.ToByte(arrLicence(5).ToString(), 16)
LicenceString = Convert.ToBase64String(LicenceBytes) '6 byteArray - passed by the user - Base64Encoded
I need its equivalent in iphone. I tried with NSData and base64 conversion but result defers.
I have used this link for conversion.
http://www.cocoadev.com/index.pl?BaseSixtyFour
I tried by creating individual bytes using memcpy and then creating an array but with no success.
What I have tried is as follows:
NSData *d1 =[#"64" dataUsingEncoding:NSUTF16StringEncoding];
NSData *d2 = [#"37" dataUsingEncoding:NSUTF16StringEncoding];
NSData *d3 = [#"81" dataUsingEncoding:NSUTF16StringEncoding];
NSData *d4 = [#"d4" dataUsingEncoding:NSUTF16StringEncoding];
unsigned char *buffer = (unsigned char*)malloc(8);
buffer[0] = [d1 bytes] ;
buffer[1] = [d2 bytes] ;
buffer[2] = [d3 bytes] ;
buffer[3] = [d4 bytes] ;
NSData *data = [NSData dataWithBytes:buffer length:4];
NSString *str = [self encodeBase64WithData:data];
free(buffer);
This results in IJCgkA== while code in .NET returns ZDeB1A==
Please note that the conversion is for first four bytes of arrLicence and the input is 64, 37, 81, d4
Try this one, i hope this will surely help you.
get-base64-nsstring-from-nsdata
unsigned char *buffer = (unsigned char*)malloc(8);
buffer[0] = [d1 bytes] ;
buffer[1] = [d2 bytes] ;
buffer[2] = [d3 bytes] ;
buffer[3] = [d4 bytes] ;
Not sure what you expect this to do. bytes return an array, and you are assigning the addresses of the arrays to char elements of your buffer. This buffer won't be filled with any of the data you expect, and the "data" from d2 will partially overwrite those from d1 etc.
Also, you shouldn't make much assumptions about the lengths of your byte arrays, especially not if using UTF-16.
In a word: You don't throw the data you expect at your conversion routine. Maybe check that in the debugger.
See code sample, Very self-explained ...
http://www.cocoadev.com/index.pl?BaseSixtyFour
Also check below SO post .
How do I do base64 encoding on iphone-sdk?
//strBusiCode = #"64-37-81-d4-39-6d";
NSArray *tmp_arr = [strBusiCode componentsSeparatedByString:#"-"];
NSMutableData *commandToSend= [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [tmp_arr count]; i++) {
byte_chars[0] = [[tmp_arr objectAtIndex:i] characterAtIndex:0];
byte_chars[1] = [[tmp_arr objectAtIndex:i] characterAtIndex:1];
whole_byte = strtol(byte_chars, NULL, 16);
[commandToSend appendBytes:&whole_byte length:1];
}
return commandToSend;
This commandToSend is then converted to base64 data.
If you look at the 2 last numbers you give as example, you will notice conversion match. Because the hi and low parts are the same.
Just swap the hi and low words.
You can use this base64.h and base64.m class to decode to base64 string.
base64.h
#import <Foundation/Foundation.h>
#interface NSData (Base64)
+ (NSData *)dataWithBase64EncodedString:(NSString *)string;
- (id)initWithBase64EncodedString:(NSString *)string;
- (NSString *) base64Encoding;
- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength;
#end
base64.m
#import "base64.h"
static char encodingTable[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
#implementation NSData (VQBase64)
- (id)initWithString:(NSString *)string {
if (self = [super init]) {
[self initWithBase64EncodedString:string];
}
return self;
}
+ (NSData *) dataWithBase64EncodedString:(NSString *) string {
return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease];
}
- (id) initWithBase64EncodedString:(NSString *) string {
NSMutableData *mutableData = nil;
if( string ) {
unsigned long ixtext = 0;
unsigned long lentext = 0;
unsigned char ch = 0;
unsigned char inbuf[4], outbuf[3];
short i = 0, ixinbuf = 0;
BOOL flignore = NO;
BOOL flendtext = NO;
NSData *base64Data = nil;
const unsigned char *base64Bytes = nil;
// Convert the string to ASCII data.
base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
base64Bytes = [base64Data bytes];
mutableData = [NSMutableData dataWithCapacity:[base64Data length]];
lentext = [base64Data length];
while( YES ) {
if( ixtext >= lentext ) break;
ch = base64Bytes[ixtext++];
flignore = NO;
if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
else if( ch == '+' ) ch = 62;
else if( ch == '=' ) flendtext = YES;
else if( ch == '/' ) ch = 63;
else flignore = YES;
if( ! flignore ) {
short ctcharsinbuf = 3;
BOOL flbreak = NO;
if( flendtext ) {
if( ! ixinbuf ) break;
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
else ctcharsinbuf = 2;
ixinbuf = 3;
flbreak = YES;
}
inbuf [ixinbuf++] = ch;
if( ixinbuf == 4 ) {
ixinbuf = 0;
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );
for( i = 0; i < ctcharsinbuf; i++ )
[mutableData appendBytes:&outbuf[i] length:1];
}
if( flbreak ) break;
}
}
}
self = [self initWithData:mutableData];
return self;
}
- (NSString *) base64Encoding {
return [self base64EncodingWithLineLength:0];
}
- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength {
const unsigned char *bytes = [self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
unsigned long ixtext = 0;
unsigned long lentext = [self length];
long ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
unsigned short i = 0;
unsigned short charsonline = 0, ctcopy = 0;
unsigned long ix = 0;
while( YES ) {
ctremaining = lentext - ixtext;
if( ctremaining <= 0 ) break;
for( i = 0; i < 3; i++ ) {
ix = ixtext + i;
if( ix < lentext ) inbuf[i] = bytes[ix];
else inbuf [i] = 0;
}
outbuf [0] = (inbuf [0] & 0xFC) >> 2;
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
outbuf [3] = inbuf [2] & 0x3F;
ctcopy = 4;
switch( ctremaining ) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for( i = 0; i < ctcopy; i++ )
[result appendFormat:#"%c", encodingTable[outbuf[i]]];
for( i = ctcopy; i < 4; i++ )
[result appendString:#"="];
ixtext += 3;
charsonline += 4;
if( lineLength > 0 ) {
if( charsonline >= lineLength ) {
charsonline = 0;
[result appendString:#"\n"];
}
}
}
return [NSString stringWithString:result];
}
#end
Please check and let me know if any issue.
I have the following code which takes a HEX code somebody entered and transforms it into HSB:
NSString *cString = [[hexText.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
// String should be 6 or 8 characters
if ([cString length] < 6) NSLog(#"UH OH");
// strip 0X if it appears
if ([cString hasPrefix:#"0X"]) cString = [cString substringFromIndex:2];
if ([cString length] != 6) NSLog(#"UH OH");
// Separate into r, g, b substrings
NSRange range;
range.location = 0;
range.length = 2;
NSString *rString = [cString substringWithRange:range];
range.location = 2;
NSString *gString = [cString substringWithRange:range];
range.location = 4;
NSString *bString = [cString substringWithRange:range];
// Scan values
unsigned int r, g, b;
[[NSScanner scannerWithString:rString] scanHexInt:&r];
[[NSScanner scannerWithString:gString] scanHexInt:&g];
[[NSScanner scannerWithString:bString] scanHexInt:&b];
float red = r / 255.0f;
float green = g / 255.0f;
float blue = b / 255.0f;
float colorArray[3];
colorArray[0] = red;
colorArray[1] = green;
colorArray[2] = blue;
int max;
int min;
min=0;
max=0;
for(int i=1; i<3; i++)
{
if(colorArray[i] > colorArray[max])
max=i;
if(colorArray[i] < colorArray[min])
min=i;
}
if(max==min)
{
h3=0;
s3=0;
b3=colorArray[0];
}
else
{
b3=colorArray[max];
s3=(colorArray[max]-colorArray[min])/(colorArray[max]);
if(max==0) // Red
h3 = (colorArray[1]-colorArray[2])/(colorArray[max]-colorArray[min])*60/360;
else if(max==1) // Green
h3 = (2.0 + (colorArray[2]-colorArray[0])/(colorArray[max]-colorArray[min]))*60/360;
else // Blue
h3 = (4.0 + (colorArray[0]-colorArray[1])/(colorArray[max]-colorArray[min]))*60/360;
}
I then have this code which does the opposite - transforms HSB into a hex code:
UIColor *forC = colourPreview.backgroundColor;
const CGFloat *c = CGColorGetComponents([forC CGColor]);
CGFloat r, g, b;
r = c[0];
g = c[1];
b = c[2];
if (r < 0.0f) r = 0.0f;
if (g < 0.0f) g = 0.0f;
if (b < 0.0f) b = 0.0f;
if (r > 1.0f) r = 1.0f;
if (g > 1.0f) g = 1.0f;
if (b > 1.0f) b = 1.0f;
hexWithoutHash = [NSString stringWithFormat:#"%02X%02X%02X",
(int)(r * 255), (int)(g * 255), (int)(b * 255)];
These should both give the same value, and most of the time it does. But sometimes I will type in a hex code such as 208DBC and it will return 1F8CBC. Any ideas? I think it's something to do with the second bit of code returning an inaccurate hex code, but not sure how to make this more accurate in this case?
Could be a floating-point precision issue. Using a float or a double does not store an exact value as using an int or long does. It stores the closest approximation of the exact value allowed by the IEEE-754 spec. The difference between the stored value and the exact value is generally very small, but it may be just big enough that when you convert back to an integer your value gets truncated to the next smaller integer. That is what is happening in your output (i.e. 0x1F = 0x20 - 1, 0x8C = 0x8D - 1).
The following code may illustrate the issue:
for (int redColor = 0; redColor < 256; redColor++) {
int originalRed = redColor;
float red = originalRed / 255.0f;
float redMultiplied = red * 255;
int newRed = (int)redMultiplied;
if (newRed != originalRed) {
NSLog(#"Value mismatch detected: origianlRed=%d, red=%f, redMultiplied=%f, newRed=%d",
originalRed, red, redMultiplied, newRed);
}
}
My setup:
I have a webview in my iPhone app, going to www.mysite.com.
Trials
If I set "backgroundcolor uicolor clearcolor" to my webview and draw an uiimage behind my webview, it won't scroll with my webview!
My problem:
I would like the image to with the webview. Also, this image should be stored in the iPhone app and not on the webserver (Of course, if I put the image on the webserver I can simply draw it as background with CSS).
What is the best way to access this image via html/javascript/Objective C, so I can see this local image as background of my website? Is there a way at all?
You have a couple of options. You can load the page with a baseURL of
[[NSBundle mainBundle] bundleURL]
or you can use javascript to set the path to your image using the following method.
[[NSBundle mainBundle] URLForResource:#"myimage" withExtension:#"png"]
Edit: Check here Link to resources inside WebView - iPhone. It looks like you will use pathForResource rather than URLForResource.
Edit 2: You may want to use Data URI's to add your own local files to the web site.
YourCode.m
#import "NSString+DataURI.h"
#import "NSData+Base64.h"
...
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *imgPath = [[NSBundle mainBundle] pathForResource:#"image" ofType:#"png"];
NSData *imgData = [NSData dataWithContentsOfFile:imgPath];
NSString *imgB64 = [[imgData base64Encoding] pngDataURIWithContent];
NSString *javascript = [NSString stringWithFormat:#"document.body.style.backgroundImage='url(%#)';", imgB64];
[webView stringByEvaluatingJavaScriptFromString:javascript];
}
The following code I DID NOT WRITE and I am not sure of the origin
NSData+Base64.h
#interface NSData (Base64)
+ (NSData *)dataWithBase64EncodedString:(NSString *)string;
- (id)initWithBase64EncodedString:(NSString *)string;
- (NSString *)base64Encoding;
- (NSString *)base64EncodingWithLineLength:(unsigned int) lineLength;
#end
NSData.Base64.m
#import "NSData+Base64.h"
static char encodingTable[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
#implementation NSData (VQBase64)
- (id)initWithString:(NSString *)string {
if ((self = [super init])) {
[self initWithBase64EncodedString:string];
}
return self;
}
+ (NSData *) dataWithBase64EncodedString:(NSString *) string {
return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease];
}
- (id) initWithBase64EncodedString:(NSString *) string {
NSMutableData *mutableData = nil;
if( string ) {
unsigned long ixtext = 0;
unsigned long lentext = 0;
unsigned char ch = 0;
unsigned char inbuf[4], outbuf[3];
short i = 0, ixinbuf = 0;
BOOL flignore = NO;
BOOL flendtext = NO;
NSData *base64Data = nil;
const unsigned char *base64Bytes = nil;
// Convert the string to ASCII data.
base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
base64Bytes = [base64Data bytes];
mutableData = [NSMutableData dataWithCapacity:[base64Data length]];
lentext = [base64Data length];
while( YES ) {
if( ixtext >= lentext ) break;
ch = base64Bytes[ixtext++];
flignore = NO;
if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
else if( ch == '+' ) ch = 62;
else if( ch == '=' ) flendtext = YES;
else if( ch == '/' ) ch = 63;
else flignore = YES;
if( ! flignore ) {
short ctcharsinbuf = 3;
BOOL flbreak = NO;
if( flendtext ) {
if( ! ixinbuf ) break;
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
else ctcharsinbuf = 2;
ixinbuf = 3;
flbreak = YES;
}
inbuf [ixinbuf++] = ch;
if( ixinbuf == 4 ) {
ixinbuf = 0;
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );
for( i = 0; i < ctcharsinbuf; i++ )
[mutableData appendBytes:&outbuf[i] length:1];
}
if( flbreak ) break;
}
}
}
self = [self initWithData:mutableData];
return self;
}
#pragma mark -
- (NSString *) base64Encoding {
return [self base64EncodingWithLineLength:0];
}
- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength {
const unsigned char *bytes = [self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
unsigned long ixtext = 0;
unsigned long lentext = [self length];
long ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
unsigned short i = 0;
unsigned short charsonline = 0, ctcopy = 0;
unsigned long ix = 0;
while( YES ) {
ctremaining = lentext - ixtext;
if( ctremaining <= 0 ) break;
for( i = 0; i < 3; i++ ) {
ix = ixtext + i;
if( ix < lentext ) inbuf[i] = bytes[ix];
else inbuf [i] = 0;
}
outbuf [0] = (inbuf [0] & 0xFC) >> 2;
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
outbuf [3] = inbuf [2] & 0x3F;
ctcopy = 4;
switch( ctremaining ) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for( i = 0; i < ctcopy; i++ )
[result appendFormat:#"%c", encodingTable[outbuf[i]]];
for( i = ctcopy; i < 4; i++ )
[result appendString:#"="];
ixtext += 3;
charsonline += 4;
if( lineLength > 0 ) {
if( charsonline >= lineLength ) {
charsonline = 0;
[result appendString:#"\n"];
}
}
}
return [NSString stringWithString:result];
}
#end
NSString+DataURI.h
#import <Foundation/Foundation.h>
#interface NSString(DataURI)
- (NSString *) pngDataURIWithContent;
- (NSString *) jpgDataURIWithContent;
#end
NSString+DataURI.m
#import "NSString+DataURI.h"
#implementation NSString(DataURI)
- (NSString *) pngDataURIWithContent;
{
NSString * result = [NSString stringWithFormat: #"data:image/png;base64,%#", self];
return result;
}
- (NSString *) jpgDataURIWithContent;
{
NSString * result = [NSString stringWithFormat: #"data:image/jpg;base64,%#", self];
return result;
}
#end
You could simply add this line
webView.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"light.jpg"]];