I use the following code to change the font size of a web view with a pinch:
- (void)changeFontSize:(FontSizeChangeType)changeType
{
if (changeType == FontSizeChangeTypeIncrease && _currentFontSize == 160) return;
if (changeType == FontSizeChangeTypeDecrease && _currentFontSize == 70) return;
if (changeType != FontSizeChangeTypeNone)
{
_currentFontSize = (changeType == FontSizeChangeTypeIncrease) ? _currentFontSize + 5 : _currentFontSize - 5;
[[NSUserDefaults standardUserDefaults] setInteger:_currentFontSize forKey:#"fontsize"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
NSString *jsString = [[NSString alloc] initWithFormat:#"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'",
_currentFontSize];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
}
The font size changes, and that is all great. However, if you leave the web view, and then come back later, the font size is not saved. I have to pinch, and then it reverts back to the chosen font size.
At the ViewDidLoad I call this, which I thought should work:
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"fontsize"] == nil)
{
_currentFontSize = 100;
}
else
{
_currentFontSize = [[NSUserDefaults standardUserDefaults] integerForKey:#"fontsize"];
}
It doesn't though. Any ideas?
You may be setting _currentFontSize out of your saved defaults in viewDidLoad, but you also have to make that same [self.webView stringByEvaluatingJavaScriptFromString:jsString] call in order to make it that size change take effect - and you have to do it after the web view has loaded since otherwise there will be no body tag.
Related
When using a category on UITextField, the "Adjust to fit" and minimum font size options may cause a memory leak, when text inside such a field exceeds its visible boundaries. I tried subclassing instead, but it did not solve the problem.
Here is how my category implementation looks like:
#implementation UITextField (custom)
static NSString *fontName = #"My-Awsome-Font";
static UIColor *color;
static UIFont *smallFont;
static UIFont *largeFont;
static NSDictionary *smallAttributes;
static NSDictionary *largeAttributes;
static NSString *placeholder;
static bool shouldModifyPlaceholder;
- (CGRect)textRectForBounds:(CGRect)bounds
{
if(largeFont == nil)
largeFont = [UIFont fontWithName:fontName size:20.0];
if(smallFont == nil)
smallFont = [UIFont fontWithName:fontName size:16.0];
if(smallAttributes == nil)
smallAttributes = #{NSFontAttributeName: smallFont};
if(largeAttributes == nil)
largeAttributes = #{NSFontAttributeName: largeFont};
// general elements
[self setBackgroundColor:[UIColor whiteColor]];
[self.layer setBorderColor:[UIColor whiteColor].CGColor];
[self.layer setBorderWidth:2.0];
self.font = largeFont;
self.layer.cornerRadius = 3;
self.clipsToBounds = YES;
//
shouldModifyPlaceholder = [self respondsToSelector:#selector(setAttributedPlaceholder:)] && ![placeholder isEqualToString:self.placeholder];
// custom elements for each size category
if (self.frame.size.width <= 90) {
if (shouldModifyPlaceholder) {
if ([self.placeholder length] > 3) {
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:smallAttributes];
}
else {
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:largeAttributes];
}
placeholder = self.placeholder;
}
return CGRectMake(bounds.origin.x + 18, bounds.origin.y + 9,
bounds.size.width - 36, bounds.size.height - 16);
}
else {
if (shouldModifyPlaceholder) {
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:largeAttributes];
placeholder = self.placeholder;
}
return CGRectMake(bounds.origin.x + 20, bounds.origin.y + 9,
bounds.size.width - 40, bounds.size.height - 16);
}
}
Recently I discovered that typing "too much" text into a UITextField caused my app to use up all the phone's memory and crash. I found out that it was due to the fact that my customisation did not work well with the "Adjust to fit" option. Removing "Adjust to fit" fixed the issue. I also set minimum font size property to the original font size to be sure. And since I used well over a day trying to find a solution, I thought I'd share this in case someone else runs into this problem.
I have seen many confusing and heck solutions to deal with status bar in iOS 7 but I liked following solution as mentioned by Apple at - https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926
....but it doesn't work! This method gets called as expected but childView is not moving down 20 pixels.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
UIView *childView = self.childController.view;
id topGuide = self.topLayoutGuide;
CGFloat topBarOffset = self.topLayoutGuide.length; //I checked this valued is 20
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings (childView, topGuide);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat: #"V:[topGuide]-20-[childView]"
options: 0
metrics: nil
views: viewsDictionary
]
];
}
}
Following works for me but I would really like to solve this problem using AutoLayout using topLayoutGuide.
- (void)layoutSubviews
{
[super layoutSubviews];
self.backgroundColor = [UIColor blackColor];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
CGRect frame = self.bounds;
frame.origin.y = 20;
frame.size.height -= 20;
self.childView.frame = frame;
} else {
self.childView.frame = self.bounds;
}
}
Any ideas why using topLayoutGuide not working? Thanks.
I'm using the CocoaLibSpotify library to load album art for Spotify search results.
Instruments reports no leaks, and static analysis isn't helping out either, and I've manually reviewed all of my code that deals with keeping track of loading the album art, yet, after loading a few hundred results, the app consumes over 100mb of memory and crashes.
I believe that CocoaLibSpotify is keeping a cache of the images in memory, but there is no way that I have found of disabling the cache. There is a "flushCaches" method, which I've been calling each time I get a memory warning, but, it is ineffective.
Here's what I'm using to load the album art, I keep a reference to all of the SPImage objects in an array, so that I can use them when serving up table view rows.
[self sendRequestToURL: #"http://ws.spotify.com/search/1/track.json" withParams: #{#"q": spotifySearchBar.text} usingMethod: #"GET" completionHandler: ^(id result, NSError *error) {
//after the search completes, re-enable the search button, replace the searchResults, and
// request the result table to reload the data
spotifySearchBar.userInteractionEnabled = YES;
[searchBar endEditing: YES];
[searchResults release];
int resultLength = [[result objectForKey: #"tracks"] count] < 100 ? [[result objectForKey: #"tracks"] count] : 100;
searchResults = [[[result objectForKey: #"tracks"] subarrayWithRange: NSMakeRange(0, resultLength)] retain];
for(int i = 0; i < 100; i++) {
[albumArtCache replaceObjectAtIndex: i withObject: [NSNull null]];
}
for(NSDictionary *trackDict in searchResults) {
NSString *trackURI = [trackDict objectForKey: #"href"];
[SPTrack trackForTrackURL: [NSURL URLWithString: trackURI] inSession: session callback: ^(SPTrack *track) {
[SPAsyncLoading waitUntilLoaded: track timeout: kSPAsyncLoadingDefaultTimeout then:^(NSArray *loadedItems, NSArray *notLoadedItems) {
if(track == nil) return;
[SPAsyncLoading waitUntilLoaded: track.album timeout: kSPAsyncLoadingDefaultTimeout then:^(NSArray *loadedItems, NSArray *notLoadedItems) {
if(track.album == nil) return;
[SPAsyncLoading waitUntilLoaded: track.album.largeCover timeout: kSPAsyncLoadingDefaultTimeout then:^(NSArray *loadedItems, NSArray *notLoadedItems) {
if(track.album.largeCover == nil) return;
if(![searchResults containsObject: trackDict]) {
NSLog(#"new search was performed, discarding loaded result");
return;
} else{
[albumArtCache replaceObjectAtIndex: [searchResults indexOfObject: trackDict] withObject: track.album.largeCover];
[resultTableView reloadRowsAtIndexPaths: #[[NSIndexPath indexPathForRow: [searchResults indexOfObject: trackDict] inSection: 0]] withRowAnimation: UITableViewRowAnimationAutomatic];
}
}];
}];
}];
}];
}
[resultTableView reloadData];
}];
And here is the code that deals with loading table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"artistCell"];
if(cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: #"artistCell"] autorelease];
}
cell.textLabel.text = [[searchResults objectAtIndex: indexPath.row] objectForKey: #"name"];
cell.detailTextLabel.text = [[[[searchResults objectAtIndex: indexPath.row] objectForKey: #"artists"] objectAtIndex: 0] objectForKey: #"name"];
if([albumArtCache objectAtIndex: indexPath.row] != [NSNull null]) {
cell.imageView.image = ((SPImage *)[albumArtCache objectAtIndex: indexPath.row]).image;
} else{
cell.imageView.image = nil;
}
return cell;
}
I really have no idea what's going wrong. Any assistance would be greatly appreciated.
First off, you should use SPSearch rather than the web API for searching.
The reason that Instruments isn't showing a memory leak is because there isn't one - CocoaLibSpotify caches albums and images internally for performance reasons. As a result, loaded album covers will also stick around.
Now, loading hundreds of 1024x1024 images into memory is obviously going to end badly. An easy way to mitigate the problem would be to not load the largest size image - it's not normally required for a table view at 1024x1024 pixels.
Otherwise, you can modify CocoaLibSpotify to be able to unload images. The easiest way to do this is to probably add a method to SPImage that basically does the opposite of -startLoading - namely, setting the image property to nil, the hasStartedLoading and loaded properties to NO and calling sp_image_release on the spImage property before setting that to NULL.
I would like to know if its possible to change the map overlay alpha based on selecting the maptype here is my code I thought might work but it doesn't seem to. Can anyone provide some incite?
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay{
TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
if(mapview.mapType == MKMapTypeHybrid) {
view.tileAlpha = 0.55;
} else if(mapview.mapType == MKMapTypeSatellite) {
view.tileAlpha = 0.0;
} else {
view.tileAlpha = 0.75;
}
return [view autorelease];
}
This code works fine
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKCircleView *circleView = [[MKCircleView alloc] initWithCircle:overlay];
CGFloat alpha;
if (mapView.mapType == MKMapTypeStandard) {
alpha = 0.5f;
} else {
alpha = 1.0f;
}
circleView.fillColor = [[UIColor blackColor] colorWithAlphaComponent:alpha];
return circleView;
}
But in mapView:viewForOverlay you set alpha based on current map type. To change alpha when map change map type you need to observe mapType property using KVO. So when map type is changing you just set new alpha for all overlays. To get view for overlay use
[mapView viewForOverlay:(id<MKOverlay>)overlay];
I have used the classes provided by apple CrumbPath.o and CrumbPathView.o, but it supports only iphone 5.0,when I try the same code with iphone 4.0 ,it does not update the route.
Code :
if (newLocation)
{
if (oldLocation.coordinate.latitude == 0.0) {
initialLocation = [[[CLLocation alloc]initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude]retain];
}
// make sure the old and new coordinates are different
if((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) &&
(oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
if (!crumbs)
{
// This is the first time we're getting a location update, so create
// the CrumbPath and add it to the map.
//
crumbs = [[CrumbPath alloc] initWithCenterCoordinate:newLocation.coordinate];
[mapView addOverlay:crumbs];
// On the first location update only, zoom map to user location
MKCoordinateRegion region =
MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000);
[mapView setRegion:region animated:YES];
}
else
{
// This is a subsequent location update.
// If the crumbs MKOverlay model object determines that the current location has moved
// far enough from the previous location, use the returned updateRect to redraw just
// the changed area.
//
// note: iPhone 3G will locate you using the triangulation of the cell towers.
// so you may experience spikes in location data (in small time intervals)
// due to 3G tower triangulation.
//
Count++;
double latitude = 0.000500 * Count;
double longitude = 0.000020 * Count;
_bean = [[TempBean alloc]init];
_bean.lat = newLocation.coordinate.latitude + latitude;
_bean.lon = newLocation.coordinate.longitude - longitude;
CLLocation *locationToDraw = [[CLLocation alloc]initWithLatitude:_bean.lat longitude:_bean.lon];
// UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Update_Loc" message:[NSString stringWithFormat:#"Lat:%f , Lon:%f",locationToDraw.coordinate.latitude,locationToDraw.coordinate.longitude] delegate:self cancelButtonTitle:#"ok" otherButtonTitles:#"Cancel",nil];
// [alert show];
// [alert release];
MKMapRect updateRect = [crumbs addCoordinate:locationToDraw.coordinate];
if (!MKMapRectIsNull(updateRect))
{
// There is a non null update rect.
// Compute the currently visible map zoom scale
MKZoomScale currentZoomScale = (CGFloat)(mapView.bounds.size.width / mapView.visibleMapRect.size.width);
// Find out the line width at this zoom scale and outset the updateRect by that amount
CGFloat lineWidth = MKRoadWidthAtZoomScale(currentZoomScale);
updateRect = MKMapRectInset(updateRect, -lineWidth, -lineWidth);
// Ask the overlay view to update just the changed area.
[crumbView setNeedsDisplayInMapRect:updateRect];
}
[self calDistance:initialLocation SecondCor:locationToDraw];
[locationToDraw release];
locationToDraw =nil;
[_bean release];
_bean = nil;
}}
If that the case then, you should try looking into your CrumbPathView.m see if the render function works correctly or not from what i see here you copied & pasted the code from Apple Docs right? In the file you should find this in method drawMapRect:zoomScale:inContext:
if (path != nil)
{
CGContextAddPath(context, path);
CGContextSetRGBStrokeColor(context, 0.0f, 0.0f, 1.0f, 0.6f);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextStrokePath(context);
CGPathRelease(path);
}
See if the path is nil or not if it's nil-ed nothing gets rendered. Have you debug into this view? The rendering happens in CrumbPathView so, you should see what happen in it.
If the path is nil then you should check the method which is called to assign its value, located above the if (path != nil)