How to react to failed thumb image loading in TTThumbsViewController? - iphone

I'm currently using TTThumbsViewController in my project. I'm getting all the urls for TTPhotoVersionLarge and TTPhotoVersionThumbnail from the web so I can't tell that a url for the thumb images will work or not.
Currently the TTThumbsViewController will just display an empty image if the thumb url can't be loaded.
So I want to be notified if a thumb fails to load and do extra error handling when that happens like:
Try to load the url for TTPhotoVersionLarge
If that fails again display an error image (which is included in the bundle)
I have looked into the three20 code but can't find a proper place where I can implement this proper error handling.

The Three20 library is great, but I've found that it's often hard to change parts of their library such as this. For instance, TTThumbsViewController basically only allows you to set the dataSource and then takes care of the rest. If you want more control, perhaps look into code such as AQGridView: http://quatermain.tumblr.com/post/528737778/aqgridview-lives-for-my-ipad-dev-camp-hackathon
I've chosen to use that over Three20's equivalent because it gives you more control over what happens with your data.
Edit: In response to using TTThumbsViewController heavily, you may want to look at this method in TTTHumbsViewController.m:
- (NSString*)URLForPhoto:(id<TTPhoto>)photo {
if ([photo respondsToSelector:#selector(URLValueWithName:)]) {
return [photo URLValueWithName:#"TTPhotoViewController"];
} else {
return nil;
}
}
It looks like you should be able to specify a different value there so long as you can find something to add to the if statement determining if the initial loading failed from the dataSource.

Related

SDWebImage clearing cache

I'm displaying a list of icons downloaded from the web with text in a table view. The icons can be changed on server side and I need to replace them as soon as new icons are getting available. I try using the following code:
[imgView setImageWithURL:url placeholderImage:[UIImage imageNamed:#"table_avatar_icon"] options:SDWebImageCacheMemoryOnly];
And call [[SDImageCache sharedImageCache] clearMemory]; In my refresh callback, but it does not purge the contents of the cache. More to it, even if I close the application and open it again the image is still there.
I found only one way to clear the cache and it is by calling [[SDImageCache sharedImageCache] clearDisk];. Which only works after I close and reopen the app.
How can I force SDWebImage to not to use disk caching?
SDImageCache *imageCache = [SDImageCache sharedImageCache];
[imageCache clearMemory];
[imageCache clearDisk];
Don't forget to put these lines of code in your didReceiveMemoryWarning, too.
Located the source if an issue. It seems that I was deceived by the Singleton pattern used in SDImageCache. The cache for extension that is used over UIImageView is being controlled by SDWebImageManager which has an instance variable of SDImageCache. If you want to clear the cache for extension you have to call its imageCache's methods like clearDisk and clearMemory.
Only following code worked for me : Swift 5.0, Xcode 11, iOS 13, SDWebImage pod 5.0
SDWebImageManager.shared.imageCache.clear(with: .all) {
print("deleted all")
}
where you choose options like SDImageCacheType.disk, SDImageCacheType.memory, SDImageCacheType.disk
Also if you want to remove specific image from cache use following:
SDWebImageManager.shared.imageCache.removeImage(forKey: "url of image", cacheType: .all)
Except SDImageCache methods, I strongly advise you to check your image urls. In my situation I tried every method for imageCache and memory issue was still continue. Crashes were occur mainly on iPhone 4s because of hardware it couldn't handle it.
Main issue was url ampersand encoding!
In example, check out these urls: first url is using "&amp" and second one is not. Because of ampersand my JSON library can't read the width and width value get much higher then it should be. That is why I had a memory issue.
1) /select.php?imageid=101961221 "&amp" ;width=100 "&amp" ;isWatermarked=true
2) /select.phpimageid=101961221&width=100&isWatermarked=true
Also the latest versions of SDWebImage library has include UIImageView+WebCache.h class and it really nicely handle cache problems.
The icons can be changed on server side,
so you need to load with refresh cached every time.
if you're using latest SDWebImage framework (5.12.x)
you can call it like this,
[imgView sd_setImageWithURL:[NSURL URLWithString:urlStr] placeholderImage:[UIImage imageNamed:#"xxx" options:SDWebImageRefreshCached];
edit at 220112.

How to switch skins (or design themes) in iOS app?

I'd like to make my iPhone app to be able to switch between skins (or design theme, or look and feel, such as wooden, metal, earth color, men's, girls, etc...).
I'll prepare some sets of skins that contains images for buttons and backgrounds, sounds, and text color, and let the user decide which set of skin they want to use by the application settings.
What is the best practice to implement this?
The conditions are:
I'd like to use Interface Builder
I need to support iOS 3.1.3 and later
I want to make the sets of skins downloadable from the internet (I can't bundle all the skins in the app, as one set of skin requires lots of images and the app file size could become huge if I do so... I also don't want to hardcode any information about specific skins.)
If a custom skin does not contain one or some elements, (such as an image or sound file), I want it to use the missing element from the default set of skin.
I don't want to create Nib files for each skin. The Nib file for one screen should be the only one in the main bundle for easier maintenance.
I'm thinking about making a superclass of all the UIViewControllers in my app and override the part that it loads Nib file, and instead of loading from the main bundle, load the resources from the skin that is saved in the Document directory... but I don't know how to do it... The default behavior of the Nib-loading methods always loads resources from the main bundle and the information about resource file names are lost after reading... :(
Thanks in advance for your help.
Am not sure about best practice .. But, if your app is not big enough, then a well structured plist is your friend.
Initially, you could choose: Metal Theme. The following should hold:
You either have a Singleton ThemeManager, or just stick an NSDictionary to one of your Singletons if appropriate.
The point behind the ThemeManager is the mapping between the asset and the theme..
Some sample code (written directly on SOF .. Don't mind Syntax mistakes):
#define kThemeMap(__x__) [[ThemeManager sharedManager] assetForCurrentTheme:__x__]
...
-(void)doUselessStuff {
UIImage* backgroundImage = [UIImage imageNamed:kThemeMap(#"FirstViewBG")];
...
}
//in the ThemeManager:
//returns the appropriate name of the asset based on current theme
-(NSString*)assetForCurrentTheme:(NSString*)asset {
//_currentTheme is an NSDictionary initialized from a plist. Plist can be downloaded, too.
NSString* newAsset = [_currentTheme objectForKey:asset];
if(newAsset == nil) {
newAsset = [_defaultTheme objectForKey:asset];
}
return asset;
}
//Let us assume the user selects Metal Theme somewhere .. Still coding ThemeManager:
-(void)selectedNewTheme:(NSString*)newTheme {
//First, get the full path of the resource .. Either The main bundle, or documents directory or elsewhere..
NSString* fullPath = ...;
self.currentTheme = [NSDictionary dictionaryWithContentsOfFile:fullPath];
}
The plist files are just a dictionary with string to string mapping... something like this:
//Default.plist
#"FirstViewBG" : #"FirstViewBG_Default.png"
#"SecondViewBG" : #"SecondViewBG_Default.png"
#"WinSound" : #"WinSound_Default.aiff"
//Metal.plist
#"FirstViewBG" : #"FirstViewBG_Metal.png"
#"SecondViewBG" : #"SecondViewBG_Metal.png"
#"WinSound" : #"WinSound_Metal.aiff"
Alternatively, you can just save the postfix, if that is good enough for you.. But, it will require string manipulation, by slicing the extension -> adding the postfix -> adding the extension ..
Or maybe make it a prefix?
You can category on UIImage with the methored imageNamed:, use the custom imageNamed: instead of the default one.
In the custom methord, selected the image by theme.

three20 TTTableViewController drag to refresh: no error screen on no connection

My app heavily relies on the three20 TTTableViewController. I am currently working on error handling, specifically related to connectivity issues. I have found that if I load with createModel that it displays a nice error msg about the status of connectivity. However, when I do a drag to refresh... it does not show me the error screen when there is no connection. Besides creating a separate datasource and model (local) and doing createModel again, is there a better solution to this issue?
you can cache the results for how long you want to by modifying the cache settings in your model's load function, as such:
request.cachePolicy = TTURLRequestCachePolicyDefault;
request.cacheExpirationAge = 3600 * 60;

Programmatically Generate PDF from HTML on iPhone

I am looking for a way to programmatically (in obj-c) generate a PDF file from a local html file. I am dynamically generating the html from user inputs, I need to create the PDF and send it to the user (via email). I am having difficulty with the PDF generation portion.
I have the code to create a PDF using CGPDFContextCreateWithURL but I am struggling with drawing the page using quartz.
I have searched extensively on SO as well as the internet to no avail.
Any help is much appreciated!
To generate a pdf from an HTML, you need to render the html into a web view, and take snapshots of the web view, and render them into an image context.
The tutorial might be helpful:
http://www.ioslearner.com/convert-html-uiwebview-pdf-iphone-ipad/
I've written a little piece of code that takes an NSAttributedString from DTCoreText, and renders it into a paged PDF file. You can find it on my GitHub Repository. It won't render images or complex html, but it should serve for most uses. Plus, if you're familiar with CoreText, you can extend my PDF frame setter to generate these items.
So what it does now: Give it an HTML string, and it will use DTCoreText to generate an NSAttributedString, then render that into a PDF. It hands back the location that it saved the PDF file in the app's Documents folder.
Why not use a WebService, send the HTML page to this and retrieve the PDF-file ?
That way you can use iTextSharp and C#, and you're done in about 2 minutes.
Plus (if you're evil) you can store and see all the data on your server.
I haven't tried this myself so i have nothing to offer concrete but I'd have to imagine there has to be an easy way to do this on iPhone due to the imaging model. I'd look deeper into the documentation.
As to pushing back with the client that is up to you but there are probably multiple reasons for wanting to keep everything local. Frankly I would not be pleased at all to here from somebody I hired that he couldn't manage this particular task. So think long and hard about this push back. Oh even if you do push back a webserver is a poor choice. I'd go back a step further and investgate why you need something in HTML in the first place.
I've never tried this so I have no idea if it'll work, but how about loading the HTML into a UIWebView, and then make the view draw itself into a PDF context? E.g.
UIWebView *webview = [[UIWebView alloc] initWithFrame:CGRectMake(...)];
[webview loadHTMLString:html baseURL:...];
Then:
- (void)webViewDidFinishLoad:(UIWebView *)webview {
CGPDFContextRef pdfContext = CGPDFContextCreateWithURL(...);
[webview.layer drawInContext:pdfContext];
...
}
I made it by following this SO: https://stackoverflow.com/a/13342906/448717
In order to maintain the same content's proportions I had to multiply the size of the WKWebView 1.25 times the printableRect's size set for the UIPrinterRenderer, as the screen points differs from the PostScript's... I guess.

Best approach for simple link within UITextView

I am creating a dictionary lookup application. The user selects a word from a UITableView and the app displays the definition. In some cases the word will be similar to another word, so I want to display "See Also:" followed by a list of similar words that when touched, bring up another definition.
In searching here on links within UITextViews, most of the answers involve linking out to the web, which is not really what I need. I simply want to get control when the user touches a word so that I can change the view.
Is UIWebView the only way to do this, or did I miss something obvious in the SDK? Also, I'd prefer to stay within the native SDK and not go the three20 route.
Thanks!
I would use another UITableView to make this work. Your list of similar words will probably be in NSArray format already, so it would be pretty easy to set up another UITableView instead of a UITextView to display the list, and given that you already have this code working for the main UITableView, you already know how to make them clickable!
A UIWebView will be the only thing that suits here I'm afraid. Data detectors are the only way to link inside of a UITextView, and they will only respond to the appropriate data types (Phone number, web page, address)...
Links can be done the normal way:
<a href='http://someotherword'>someotherword</a>
Setup the webviewdelegate to snag any link requests (and prevent them from being opened in the browser) so that you can open them in your own handler:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
{
if(navigationType == UIWebViewNavigationTypeOther) return YES; // Allow direct loading
NSString *myWord = [[request URL] host];
// do something with myWord... say open another word
return NO; // Don't let the browser actually perform this navigation
}