I am new to iOS development so please bear with me.
I am trying to create a basic photo gallery but ran into a problem.
When I started out with the project I just included all the images in my project.
Now after having a lot more images(400+) I started loading them from a server.
I made an array of images using the following line of code:
[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.testsite.com/testPic.png"]]]
Obviously making the user wait for an array of 400+ images to load from a server is unacceptable.
So my question is if I included one image in my project that said something like "Loading", how could I display that image until the actual image loaded from the server?
I'm making a basic grid-style photo gallery using a table-view and scroll-view. It loads up a few rows of small(thumbnail) images and when you click one it makes it full screen.
I'm using Xcode 4.3, ARC, and storyboards if that helps!
Sorry if this is confusing!
-Shredder2794
The simplest way of doing this is to use AFNetworking, which provides setImageWithURL:placeholderImage: in a category on UIImageView (also a method with success/failure handlers).
A method is to subclass UIImageView. When you allocate it you put a default image ( the loading one) or a UIActivityIndicator, download the image you want to display ( in a separate thread) and when the image is downloaded display it. Have a look to NSURLRequest and NSURLConnection for the image download.
* EDIT *
Here is an example of code I developed. You can use this as a start point to develop your own loading image class. This class can be improved by using NSThread for the image loading.
// ImageLoader.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
/**
* #brief Class that loads an UIImage from a server
* #author Nicolas
*/
#interface ImageLoader : UIView
{
#private
NSURLConnection *connection;
NSMutableData *data;
NSString *path;
UIActivityIndicatorView *loading;
UIImageView *imageView;
}
#property (nonatomic, retain) UIActivityIndicatorView *loading;
#property (nonatomic, retain) NSURLConnection *connection;
#property (nonatomic, retain) NSMutableData *data;
#property (nonatomic, retain) NSString *path;
#property (nonatomic, retain) UIImageView *imageView;
/**
* Load an image from a server an display it
* #param URL URL to get the image
* #param chemin path to save the image
* #author Nicolas
*/
- (void)loadImageFromUrl:(NSString *)URL forPath:(NSString *)chemin;
#end
// ImageLoader.m
#import "ImageLoader.h"
#implementation ImageLoader
#synthesize path, connection, data, loading, imageView;
- (id)init
{
self = [super init];
[self setUserInteractionEnabled:YES];
return self;
}
- (void)loadImageFromUrl:(NSString *)URL forPath:(NSString *)chemin
{
//if (connection != nil) [connection release];
//if (data != nil) [data release];
//if (path != nil) [path release];
self.path = chemin;
if ([[NSFileManager defaultManager] fileExistsAtPath:chemin])
{
if (imageView != nil)
{
[imageView removeFromSuperview];
[imageView release];
}
imageView = [[UIImageView alloc] initWithFrame:self.bounds];
imageView.image = [UIImage imageWithContentsOfFile:chemin];
[self addSubview:imageView];
}
else
{
loading = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)];
loading.center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
[loading setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
[loading startAnimating];
[self addSubview:loading];
NSURL *myURL = [NSURL URLWithString:[URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:myURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0f];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
}
#pragma mark -
#pragma mark NSURLConnection protocol
- (void)connection:(NSURLConnection *)_connection didReceiveData:(NSData *)_data
{
if (data == nil)
{
data = [[NSMutableData alloc] initWithCapacity:2048];
}
[data appendData:_data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)_connection
{
[data writeToFile:self.path atomically:YES];
if (imageView != nil)
{
[imageView removeFromSuperview];
[imageView release];
}
imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:data]];
imageView.frame = self.bounds;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[loading stopAnimating];
[loading setHidden:YES];
[self addSubview:imageView];
}
- (void)connection:(NSURLConnection *)_connection didFailWithError:(NSError *)error
{
[loading stopAnimating];
[loading release];
}
#pragma mark - Memory management
- (void)dealloc
{
[connection cancel];
[connection release];
[imageView release];
[path release];
[data release];
[super dealloc];
}
#end
Related
hello guys i am downloading images for each product from my server and set it up inside a custom cell of my UITableView and caching them on disk after scrolling down the UITableView images are miss placed what i mean the new cells takes the old values of the previous cells after searching of this topic i found different solution either to use prepareForReuse or checking on the cell if its nil or not both of them does not fit my case.
CODE:
MyCustomCell.h
#import <UIKit/UIKit.h>
#import "mirsaProduct.h"
#import "SVProgressHUD.h"
#import "AFNetworking.h"
#import "UIWebView+AFNetworking.h"
#import "SDWebImageCompat.h"
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageDownloader.h"
#import "SDImageCache.h"
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageManager.h"
#import "SDWebImageDecoder.h"
#import "SDWebImagePrefetcher.h"
#interface mirsaProductTableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *mirsaProductTitle;
#property (weak, nonatomic) IBOutlet UITextView *mirsaProductDescription;
#property (weak, nonatomic) IBOutlet UIActivityIndicatorView *mirsaProductActivityIndicatorView;
#property (weak, nonatomic) IBOutlet UIImageView *mirsaProductLogoImageView;
#end
**MyCustomCell.m**
#import "mirsaProductTableViewCell.h"
#implementation mirsaProductTableViewCell
#synthesize mirsaProductTitle = _mirsaProductTitle;
#synthesize mirsaProductDescription = _mirsaProductDescription;
#synthesize mirsaProductLogoImageView = _mirsaProductLogoImageView;
#synthesize mirsaProductActivityIndicatorView = _mirsaProductActivityIndicatorView;
-(void)prepareForReuse
{
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
myTableViewController.h
#import <UIKit/UIKit.h>
#import "mirsaCategories.h"
#import "sharedManagers.h"
#import "mirsaProductTableViewCell.h"
#import "constant.h"
#import "SVProgressHUD.h"
#import "AFNetworking.h"
#import "UIWebView+AFNetworking.h"
#import "SDWebImageCompat.h"
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageDownloader.h"
#import "SDImageCache.h"
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageManager.h"
#import "SDWebImageDecoder.h"
#import "SDWebImagePrefetcher.h"
#import "mirsaProduct.h"
#import "mirsaDetailedProductViewController.h"
#import "mirsaProductDetailsTableViewController.h"
#interface mirsaProductsTableViewController : UITableViewController
#property (nonatomic,strong) mirsaCategories *currentCategory;
#property (nonatomic,strong) NSMutableArray *listOfProducts;
#property (nonatomic,strong) mirsaProduct *currentProduct;
#end
myTableViewController.m
due to not make the question too long i just import the cellOfRowAtIndexPath and the relevant method to it.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"mirsaProductCell";
mirsaProductTableViewCell *cell = (mirsaProductTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[mirsaProductTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] ;
cell.mirsaProductLogoImageView.layer.cornerRadius = 50.0f;
cell.mirsaProductLogoImageView.layer.borderWidth = 1.5f;
cell.mirsaProductLogoImageView.clipsToBounds = YES;
cell.mirsaProductLogoImageView.layer.borderColor = [UIColor colorWithRed:redRedColor green:redGreenColor blue:redBlueColor alpha:ALFA].CGColor;
mirsaProduct *currentProduct = [self.listOfProducts objectAtIndex:indexPath.row];
cell.mirsaProductTitle.text = [NSString stringWithFormat:#"Product Code:%#",currentProduct.mirsaProductCode];
cell.mirsaProductDescription.text = [NSString stringWithFormat:#"Description: %#",currentProduct.mirsaProductDescription];
[self downloadProductLogoImageView:cell :indexPath.row];
for (int i = 0; i < currentProduct.mirsaProductUrlImage.count; i++) {
[self downloadImageWithCached:currentProduct.mirsaProductUrlImage[i] :cell :indexPath.row :(NSInteger)i];
}
}
else
{
cell.mirsaProductLogoImageView.layer.cornerRadius = 50.0f;
cell.mirsaProductLogoImageView.layer.borderWidth = 1.5f;
cell.mirsaProductLogoImageView.clipsToBounds = YES;
cell.mirsaProductLogoImageView.layer.borderColor = [UIColor colorWithRed:redRedColor green:redGreenColor blue:redBlueColor alpha:ALFA].CGColor;
mirsaProduct *currentProduct = [self.listOfProducts objectAtIndex:indexPath.row];
cell.mirsaProductTitle.text = [NSString stringWithFormat:#"Product Code:%#",currentProduct.mirsaProductCode];
cell.mirsaProductDescription.text = [NSString stringWithFormat:#"Description: %#",currentProduct.mirsaProductDescription];
[self downloadProductLogoImageView:cell :indexPath.row];
for (int i = 0; i < currentProduct.mirsaProductUrlImage.count; i++) {
[self downloadImageWithCached:currentProduct.mirsaProductUrlImage[i] :cell :indexPath.row :(NSInteger)i];
}
}
return cell;
}
-(void)downloadProductLogoImageView:(mirsaProductTableViewCell *)cell :(NSInteger )index;
{
#autoreleasepool {
[cell.mirsaProductActivityIndicatorView startAnimating];
mirsaProduct *currentProduct = [self.listOfProducts objectAtIndex:index];
NSString *cachedKey = currentProduct.mirsaProductImage;
SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:#"productLogoImageView"];
[imageCache queryDiskCacheForKey:cachedKey done:^(UIImage *image, SDImageCacheType cacheType)
{
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
[cell.mirsaProductActivityIndicatorView stopAnimating];
cell.mirsaProductLogoImageView.image = image;
});
}else{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:currentProduct.mirsaProductImage];
[[SDWebImageDownloader sharedDownloader]downloadImageWithURL:url options:SDWebImageDownloaderLowPriority progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if (finished && image){
dispatch_async(dispatch_get_main_queue(), ^{
[imageCache setMaxCacheAge:60*60*24];
[imageCache storeImage:image
recalculateFromImage:NO
imageData:data
forKey:currentProduct.mirsaProductImage
toDisk:YES];
});
[cell.mirsaProductActivityIndicatorView stopAnimating];
cell.mirsaProductLogoImageView.image = image;
}
}];
});
}
}];
}
}
some links that i found on stack overflow Tableview images chaging when scrolling using custom tableview, Incorrect cell data display when scrolling UITableView
Hi as far i go through your code there two major changes that need to be done to solve your problem
if (cell == nil) {
cell = [[mirsaProductTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] ;
cell.mirsaProductLogoImageView.layer.cornerRadius = 50.0f;
in this lines of code don't use nil instead of use cell Identifier
to download image you don't need to write that much of code just write
[imageView sd_setImageWithURL:[NSURL URLWithString:#"http://www.example.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
A third party library like SDWebImage is a reasonable idea, though a simple home-rolled version is not too tough to create. Instead of a regular image view in the cell, use your own subclass, something like this:
// .h
#import <UIKit/UIKit.h>
#interface AsynchImageView : UIImageView
- (void)setImageFromUrl:(NSString *)url placeholder:(UIImage *)placeholder;
#end
// .m
#import "AsynchImageView.h"
#implementation AsynchImageView
- (void)setImageFromUrl:(NSString *)url placeholder:(UIImage *)placeholder {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLCache *cache = [NSURLCache sharedURLCache];
NSCachedURLResponse *cachedResponse = [cache cachedResponseForRequest:request];
if (cachedResponse) {
[self setImageFromData:cachedResponse.data];
} else {
self.image = placeholder;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
[cache storeCachedResponse:[[NSCachedURLResponse alloc] initWithResponse:response data:data] forRequest:request];
[self setImageFromData:data];
}
}] resume];
}
}
- (void)setImageFromData:(NSData *)data {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.image = image;
});
}
#end
This checks to see if the image has been downloaded already, if it hasn't it sets a placeholder image and starts a download. Upon completion, it sets the image to the downloaded one and caches it.
Remove all of the other image code when configuring the cell, and do this:
NSString *url = currentProduct.mirsaProductImage;
[cell.mirsaProductLogoImageView setImageFromUrl: placeholder:];
My Requirement is download all images in application memory and display it from local if its available.
Below is my code to access image from local and if its not available then it will download then display.
[cell.imgProfilePic processImageDataWithURLString:cData.PICTURE];
I have made custom UIImageView class
DImageView.h
#import <UIKit/UIKit.h>
#interface DImageView : UIImageView
#property (nonatomic, strong) UIActivityIndicatorView *activityView;
- (void)processImageDataWithURLString:(NSString *)urlString;
+ (UIImage *)getSavedImage :(NSString *)fileName;
#end
DImageView.m
#import "DImageView.h"
#define IMAGES_FOLDER_NAME #"DImages"
#implementation DImageView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{ }
return self;
}
- (void)dealloc
{
self.activityView = nil;
[super dealloc];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self initWithFrame:[self frame]];
}
return self;
}
- (void)processImageDataWithURLString:(NSString *)urlString
{
#autoreleasepool
{
UIImage * saveImg =[DImageView getSavedImage:urlString];
if (saveImg)
{
#autoreleasepool
{
dispatch_queue_t callerQueue = dispatch_get_main_queue();
dispatch_async(callerQueue, ^{
#autoreleasepool{
[self setImage:saveImg];
}
});
}
}
else
{
[self showActivityIndicator];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
dispatch_queue_t callerQueue = dispatch_get_main_queue();
dispatch_queue_t downloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
__block NSError* error = nil;
dispatch_async(downloadQueue, ^{
#autoreleasepool
{
NSData * imageData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
if (!error)
{
dispatch_async(callerQueue, ^{
#autoreleasepool {
UIImage *image = [UIImage imageWithData:imageData];
[self setImage:image];
[self hideActivityIndicator];
[self saveImageWithFolderName:IMAGES_FOLDER_NAME AndFileName:urlString AndImage:imageData];
}
});
}
}
});
dispatch_release(downloadQueue);
}
}
}
- (void) showActivityIndicator
{
self.activityView = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.activityView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
self.activityView.hidesWhenStopped = TRUE;
self.activityView.backgroundColor = [UIColor clearColor];
self.activityView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self addSubview:self.activityView];
[self.activityView startAnimating];
}
- (void) hideActivityIndicator
{
CAAnimation *animation = [NSClassFromString(#"CATransition") animation];
[animation setValue:#"kCATransitionFade" forKey:#"type"];
animation.duration = 0.4;;
[self.layer addAnimation:animation forKey:nil];
[self.activityView stopAnimating];
[self.activityView removeFromSuperview];
for (UIView * view in self.subviews)
{
if([view isKindOfClass:[UIActivityIndicatorView class]])
[view removeFromSuperview];
}
}
- (void)saveImageWithFolderName:(NSString *)folderName AndFileName:(NSString *)fileName AndImage:(NSData *) imageData
{
#autoreleasepool{
NSFileManager *fileManger = [[NSFileManager defaultManager] autorelease];
NSString *directoryPath = [[NSString stringWithFormat:#"%#/%#",[DImageView applicationDocumentsDirectory],folderName] autorelease];
if (![fileManger fileExistsAtPath:directoryPath])
{
NSError *error = nil;
[fileManger createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
}
fileName = [DImageView fileNameValidate:fileName];
NSString *filePath = [[NSString stringWithFormat:#"%#/%#",directoryPath,fileName] autorelease];
BOOL isSaved = [imageData writeToFile:filePath atomically:YES];
if (!isSaved)DLog(#" ** Img Not Saved");
}
}
+ (NSString *)applicationDocumentsDirectory
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
+ (UIImage *)getSavedImage :(NSString *)fileName
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
fileName = [DImageView fileNameValidate:fileName];
NSFileManager * fileManger = [[NSFileManager defaultManager] autorelease];
NSString * directoryPath = [[NSString stringWithFormat:#"%#/%#",[DImageView applicationDocumentsDirectory],IMAGES_FOLDER_NAME] autorelease];
NSString * filePath = [[NSString stringWithFormat:#"%#/%#",directoryPath,fileName] autorelease];
if ([fileManger fileExistsAtPath:directoryPath])
{
UIImage *image = [[[UIImage imageWithContentsOfFile:filePath] retain]autorelease];
if (image)
return image;
else
return nil;
}
[pool release];
return nil;
}
+ (NSString*) fileNameValidate : (NSString*) name
{
name = [name stringByReplacingOccurrencesOfString:#"://" withString:#"##"];
name = [name stringByReplacingOccurrencesOfString:#"/" withString:#"#"];
name = [name stringByReplacingOccurrencesOfString:#"%20" withString:#""];
return name;
}
#end
Everything is working fine with smooth scrolling as well as asyncImage download in background.
The issue is when i scroll UITableview application memory is continuously increase and after some time i got Receive memory waring 2/3 time then application crash.
When i use AsyncImageView class that time memory not increase and its working fine. But due to app requirement i saved all images to Document Directory and display from it if its available.
i have tried with #autoreleasepool and release some variable but not getting success.
I appreciated if any one have the solution to manage memory management.
**ARC is off in my application.**
It's possible that UIImagePNGRepresentation returns non-autoreleased object - you can try to release it and see if that results in a crash. Obviously you are not releasing something, but nothing other than the image representation appears obvious.
A few other comments:
run your app in Instruments, using the ObjectAlloc tool, and it should be immediately obvious what objects are not dealloced. If you don't know Instruments, well, its time now to learn it.
you can 'track' objects and get a message when they are dealloced using ObjectTracker - however it was designed for ARC so you may need to tweak it. If you use it you would see a message when each of your objects are dealloced
when the table view is done with a cell, there is a delegate method that you can receive that tells you so, and you can then nil (release) and objects the cell retains
your use of downloadQueue is bizarre - create it once in your instance as an ivar, use it as you need, and in dealloc release it
you hide the activity spinner on the main queue, but don't start it on the main queue
you command the activity view to remove itself from its superview, but then look for in in the subviews and try to remove it there:
[self.activityView removeFromSuperview];
for (UIView * view in self.subviews)
{
if([view isKindOfClass:[UIActivityIndicatorView class]])
[view removeFromSuperview];
}
In the end, Instruments is what you want. You can read up more about it here, or just google and you will surely find a slew of blogs to read.
Yes Finally i have resolved it.
The code which is in Question is working fine now. but Without release some objects and #autoreleasepool block which is in code, memory was increase continuously during scroll UITableView.
From the Instrument i found that memory increase in UILableView and UIImageView. I am using Custom UITableViewCell and in that file i havnt implement dealloc method. So When i have implement dealloc method in UITableViewCell .m file and release & nil all object.
After that memory not increase during scroll TableView and its Resolved the issue.
As per my Understanding there is an issue in your "getSavedImage" Method you have to manage memory Manually instead of 'autorelease' so as My suggestion is use
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath]
and also release it after use of it. means after '[self setImage:saveImg];'
[saveImg release]
instead of this.
[[UIImage imageWithContentsOfFile:filePath] retain];
'Don't Use Autorelease because it has staying in memory until pool not drain' and just because of this you got an memory issue.
I have a problem with webview.
I made a simple webbrowser for Osx, within I have to hide nvigation bar , menu amd right click and the user can go only in one specific url..
all this is ok but I need that allow _blank target.. i mean .. I have some link woth target _blank, so to open in a new window, but it does no work and i don't know hot to allow this.
this is my code for DataOAppDelegate.h:
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#interface DataOAppDelegate : NSObject <NSApplicationDelegate,NSWindowDelegate>
{WebView *WebView;
//other instance variable
}
#property (assign) IBOutlet NSWindow *window;
#property (retain, nonatomic) IBOutlet WebView *myWebView;
#end
and code for DataOAppDelegate.m
#import "DataOAppDelegate.h"
//#import <WebKit/WebKit.h>
#implementation DataOAppDelegate
#synthesize window;
#synthesize myWebView;
//your function etc
-(void)awakeFromNib{
NSString *urlText = #"http://website.com";
[[myWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlText]]];
[myWebView setDrawsBackground:NO];
[window setDelegate:self];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[myWebView setUIDelegate:self];
NSString *urlText = #"http://website.com";
[[myWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlText]]];
}
- (WebView *)myWebView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
{
NSLog(#"sss%#",sender);
NSUInteger windowStyleMask = NSClosableWindowMask |
NSMiniaturizableWindowMask |
NSResizableWindowMask |
NSTitledWindowMask;
NSWindow* webWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO];
WebView* newWebView = [[WebView alloc] initWithFrame:[webWindow contentRectForFrameRect:webWindow.frame]];
[newWebView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
[webWindow setContentView:newWebView];
[webWindow center];
[webWindow makeKeyAndOrderFront:self];
[[newWebView mainFrame] loadRequest:request];
return newWebView;
}
- (void)launchSoftWithBundleID:(NSString *)softPath
{
NSBundle *softBundle = [NSBundle bundleWithPath:softPath];
NSString *bundleID = [softBundle bundleIdentifier];
//
NSTask *softTask = [[NSTask alloc] init];
[softTask setLaunchPath:softPath];
[softTask launch];
//
NSArray *array = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleID];
if ([array count] > 0)
{
NSRunningApplication *runningApp = [array objectAtIndex:0];
[runningApp activateWithOptions:NSApplicationActivateIgnoringOtherApps];
}
}
WebViews have delegate methods to do so: decidePolicyForNavigationAction and decidePolicyForNewWindowAction (documentation).
- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener {
if ([sender isEqual:self.YourVebView]) {
[listener use];
}
else {
[[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
[listener ignore];
}
}
- (void)webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request newFrameName:(NSString *)frameName decisionListener:(id<WebPolicyDecisionListener>)listener {
[[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
[listener ignore];
}
Note: don't forget to set the policy delegate for your WebView.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
This is probably is pretty easy, but I'm stuck with it today.
The idea is that in my browser, I've create uiwebview and I want to implimate address bar in popover with it own class.
I can get the url from UItextfield from popover class to webview class, but when I get it uiwebview get lazy and it doesn't load it.
When I check it, debuger says that webview is null.
This is ViewController.h
#import <UIKit/UIKit.h>
#import "AdressBar.h"
#import "mypopoverController.h"
#interface ViewController : UIViewController<AddressbarDelegate>
{
UIWebView* mWebView;
mypopoverController *popoverController;
}
#property (nonatomic, retain) IBOutlet UIWebView* webPage;
#end
This is ViewController.m:
#import "mypopoverController.h"
#import "MyOwnPopover.h"
#import "ViewController.h"
#import "AdressBar.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize webPage = mWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
addressBar = [[AdressBar alloc] init];
addressBar.delegate = self;
[edittext addTarget:self action:#selector(showPopoverAdressBar:forEvent:) forControlEvents:UIControlEventTouchUpInside];
NSURL *url = [NSURL URLWithString:#"http://www.google.lv"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[mWebView setScalesPageToFit:YES];
[mWebView setDelegate:self];
[mWebView loadRequest:request];
}
-(void)loadReceivedAddress:(NSURLRequest *)url{
NSLog(#"url= %#", url);//there url always is not null and mWebView should load it
if(mWebView != nil){
[mWebView loadRequest:url];
}else{
NSLog(#"mWebView is null");//...but there it say's that it's null
}}
-(void)showPopoverAdressBar:(id)sender forEvent:(UIEvent*)event
{
AdressBar *popoverControllesr = [[AdressBar alloc]init];
popoverControllesr.view.frame = CGRectMake(0,0, 600, 45);
popoverControllesr.view.backgroundColor = [UIColor whiteColor];
popoverController = [[mypopoverController alloc] initWithContentViewController:popoverControllesr];
popoverController.cornerRadius = 20;
if(_titles!=NULL){
popoverController.titleText = _titles;}else{
popoverController.titleText = #"Loading...";
}
popoverControllesr.address.text = absoluteString;
popoverController.popoverBaseColor = [UIColor orangeColor];
popoverController.popoverGradient= YES;
popoverController.arrowPosition = TSPopoverArrowPositionHorizontal;
[popoverController showPopoverWithTouch:event];
}
#end
This is AdressBar.h
#import <UIKit/UIKit.h>
#protocol AddressbarDelegate <NSObject>
#required
-(void)loadSomethingFromAddressBar:(NSURLRequest*)request;
#end
#interface AdressBar : UIViewController{
IBOutlet UIButton *cancel;
}
#property (nonatomic, retain) IBOutlet UITextField *address;
#property (nonatomic, retain) NSURLRequest *request;
#property(nonatomic, weak) id <AddressbarDelegate> delegate;
#end
This is AdressBar.m:
#import "AdressBar.h"
#import "ViewController.h"
#interface AdressBar ()
#end
#implementation AdressBar
#synthesize delegate = delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[_address setDelegate:self];
_address.clearButtonMode =
UITextFieldViewModeWhileEditing;
_address.keyboardType = UIKeyboardTypeURL;
[_address addTarget:self
action:#selector(loadAddresss)
forControlEvents:UIControlEventEditingDidEndOnExit];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)loadAddresss {
NSString* urlString = _address.text;
NSURL* url = [NSURL URLWithString:urlString];
if(!url.scheme)
{
NSString* modifiedURLString = [NSString stringWithFormat:#"http://%#", urlString];
url = [NSURL URLWithString:modifiedURLString];
}
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSLog(#"request= %#", request);
NSLog(#"text= %#", urlString);
if (request!=nil) {
if(delegate!=nil)
{NSLog(#"delegate not nil");
[delegate loadSomethingFromAddressBar:request];
}else{
NSLog(#"delegate is nil");//There delegate always is nil
}
}
}
#end
Make sure that your web view Outlet & Delegate is correctly conncted.
There no space between URL Address because I also face the same issue. Try to open that url in web-Browser.
If this is your view hierarchy
---- In ViewController
---- Showing AddressBar View in POPOver
---- Remove POPover and display ViewController's web view
I suggest create a custom delegate method in AddressBar and when you remove the popOver trigger the delegate method. Implement the delegate in your ViewContrller and call loadSomethingFromAddressBar in that implemented delegate method
Note : make sure you have connected you webpage IBOutlet to your nib file.
// In Adressbar.h
#protocol AddressbarDelegate <NSObject>
#required
-(void)loadYourWebViewNow:(NSURLRequest*)request;
#end
#interface Addressbar : UIViewController
{
}
#property(nonatomic, weak) id <AddressbarDelegate>
// In Adressbar.m
- (void)loadAddresss {
NSString* urlString = _address.text; //geting text from UItextField
NSURL* url = [NSURL URLWithString:urlString];
if(!url.scheme)
{
NSString* modifiedURLString = [NSString stringWithFormat:#"http://%#", urlString];
url = [NSURL URLWithString:modifiedURLString];
}
NSURLRequest *request = [NSURLRequest requestWithURL:url];
if (request!=nil) {
NSLog(#"request is good");
if(_delegate!=nil)
{
[_delegate loadYourWebViewNow:request];
}
}
// In your ViewController.h
#interface ViewController : UIViewController<AddressbarDelegate>
{
//AdressBar *addressBar;
}
// In your ViewController.m implement the delegate method and set the delegate
#implementation ViewController
-(void)viewDidLoad
{
Remove these two below lines on viewDidLoad
//addressBar = [[Adressbar alloc] init];
//addressbar.delegate = self;
}
-(void)loadYourWebViewNow:(NSURLRequest*)request
{
[self loadSomethingFromAddressBar:request];
}
-(void)showPopoverAdressBar:(id)sender forEvent:(UIEvent*)event
{
AdressBar *popoverControllesr = [[AdressBar alloc]init];
popoverControllesr.delegate = self; // set the delegate here to this object.
popoverControllesr.view.frame = CGRectMake(0,0, 600, 45);
popoverControllesr.view.backgroundColor = [UIColor whiteColor];
popoverController = [[mypopoverController alloc] initWithContentViewController:popoverControllesr];
popoverController.cornerRadius = 20;
if(_titles!=NULL){
popoverController.titleText = _titles;}else{
popoverController.titleText = #"Loading...";
}
popoverControllesr.address.text = absoluteString;
popoverController.popoverBaseColor = [UIColor orangeColor];
popoverController.popoverGradient= YES;
popoverController.arrowPosition = TSPopoverArrowPositionHorizontal;
[popoverController showPopoverWithTouch:event];
}
So guys I'm using this function to take a picture of the UIWebView which loading an e-book and display it while the user render the pages
-(UIImage*)captureScreen:(UIView*) viewToCapture
{
UIGraphicsBeginImageContextWithOptions(viewToCapture.bounds.size, viewToCapture.opaque, 0.0);
[viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
but the problem there is a delay (0.5 second) happened to get the image .. when i test in the time profiler the instruments point to this line [viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()]; as the one which causing the delay .. so any advice , suggestion how could I come over this delay.
Thanks in advanced.
Notes :
1.Im using UIPageController for the book effect.
2.to check what I have tried please check this link
The best way of solving this is to use delegates. Meaning that if you tell a delegate method to make the image, it can do it in the background and tell your view "Hey, now I got you an image for XXX" (depending on how you implement it.
This way you can load your view with the e-books in it and just show a loader in the middle of the book with a default background. When the image is done, you update the view for the book with the correct image and remove the loader.
Much like how Apple's iBooks and any other good application does.
Example from one of my own projects (adapted to your need, yet for use in UITableViewController):
BookDelegate.h
#import <Foundation/Foundation.h>
#class Book;
#protocol BookDelegate <NSObject>
#optional
- (void)didRecieveImageForBook:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath;
#end
Book.h
#import <Foundation/Foundation.h>
#import "BookDelegate.h"
#interface Book : NSOperation <NSObject>
{
id <BookDelegate> delegate;
SEL didRecieveImageForBookSelector;
}
#property (strong, nonatomic) id delegate;
#property (assign) SEL didRecieveImageForBookSelector;
- (NSString*)getBookImageForBookId:(int)BookId externalRefference:(NSString*)url indexPath:(NSIndexPath*)indexPath;
- (id)delegate;
// Delegate methods
- (void)didRecieveImageForBook:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath;
#end
Book.m
#import "Book.h"
#import <objc/runtime.h>
#implementation Book
static char kAssociationKey;
#synthesize didRecieveImageForBookSelector;
#synthesize delegate;
- (id)init
{
if (self = [super init])
{
[self setDidRecieveImageForBookSelector:#selector(didRecieveImageForBook:indexPath:)];
}
return self;
}
#pragma mark -
#pragma mark The default delegate functions
- (void)didRecieveImageForBook:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath
{
NSLog(#"********************************************************");
NSLog(#"*** PLEASE IMPLEMENT THE FOLLOWING DELEGATE FUNCTION ***");
NSLog(#"*** didRecieveImageForBook:indexPath: ***");
NSLog(#"********************************************************");
}
#pragma mark -
#pragma mark Function for fechting images
// This method is not adapted to what YOU need, but left my code here in case it might help you out.
- (NSString*)getBookImageForBookId:(int)bookId externalRefference:(NSString*)url indexPath:(NSIndexPath*)indexPath
{
NSString *ext = [[url lastPathComponent] pathExtension];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *imagePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#%d.%#", APP_BookIMAGEPEFIX, BookId, ext]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:imagePath];
if (fileExists)
{
return imagePath;
}
else {
NSURL *theUrl = [NSURL URLWithString:url];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:theUrl];
[request setDidFinishSelector:#selector(BookImageFetched:)];
[request setDidFailSelector:#selector(processFailed:)];
[request setTimeOutSeconds:60];
[request setDownloadDestinationPath:imagePath];
[request setDelegate:self];
[request startAsynchronous];
objc_setAssociatedObject(request, &kAssociationKey, indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return #"";
}
}
- (void)BookImageFetched:(ASIHTTPRequest *)request
{
NSIndexPath *indexPath = objc_getAssociatedObject(request, &kAssociationKey);
NSString *imagePath = request.downloadDestinationPath;
[[self delegate] performSelector:self.didRecieveImageForBookSelector withObject:imagePath withObject:indexPath];
}
#pragma mark -
#pragma mark delegate functions
- (id)delegate
{
return delegate;
}
- (void)setDelegate:(id)newDelegate
{
delegate = newDelegate;
}
#pragma mark -
#end
You could use GCD - I noticed a a step missing from the GCD in the link you gave. This is what I use to asynchronously get an image and notify when its ready and it works fine:
dispatch_queue_t concurrent = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(concurrent, ^{
__block UIImage *image = nil;
dispatch_sync(concurrent, ^{
//put code to grab image here
});
dispatch_sync(dispatch_get_main_queue(), ^{
//this gets called when the above is finshed
//you should also check if the image is nil or not
});
});
hope it helps
for the record - I use this for taking UIView snapshots and always try to put my target inside a parentview even if it is temporarily - it seems to speed it up.
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
not sure if it helps or if you are using the same approach. I hope you solve this soon :)