I'm experimenting with a magazine kind of iPad app that I develop and I ran into a memory issue.
There are multiple UIWebView-s on a UIScrollView. Every UIWebView displays an html page each from html file source. Unfortunately I couldn't find the solution to handle the web views to load them smoothly in the background once the app loaded, instead of that it loads and fires every html pages with all its javascripts and images, because I want them to be visible once I swipe through on them, hovewer I get "Received memory warning" message at 30 panels and the app breaks.
The code I use to display the web views is this:
NSInteger numberOfViews = 47;
for (int i = 0; i < numberOfViews; i++) {
CGFloat yOrigin = i * 768;
DetailViewController *detailViewController2 = [[DetailViewController alloc] initWithNibName:#"Detail" bundle:nil];
[detailViewController2.view setFrame:CGRectMake(yOrigin, 0, 768, 1024)];
detailViewController2.view.clipsToBounds = YES;
[scroll.view insertSubview:detailViewController2.view atIndex:0];
}
scroll.scrollView.contentSize = CGSizeMake(768 * numberOfViews,1004);
scroll.pageControl.numberOfPages = numberOfViews;
self.view.clipsToBounds = YES;
self.view.clearsContextBeforeDrawing = YES;
CGRect frame;
frame.origin.x = scroll.scrollView.frame.size.width * scroll.pageControl.currentPage;
frame.origin.y = 0;
frame.size = scroll.scrollView.frame.size;
[scroll.scrollView scrollRectToVisible:frame animated:NO];
self.scrollViewController = scroll;
[self.view insertSubview:scroll.view atIndex:0];
- (void)dealloc {
[scrollViewController release];
[tableOfContentsViewController release];
[portrait release];
[tableOfContents1 release];
[start release];
[super dealloc];
}
The code that displays the html content is inside the DetaildViewController:
NSString *htmlPath = [[NSBundle mainBundle] pathForResource:htmlfilename
ofType:#"html"
inDirectory:#"/htdocs" ];
NSString *html = [NSString stringWithContentsOfFile:htmlPath
encoding:NSUTF8StringEncoding
error:nil];
web.delegate = self;
[web setMultipleTouchEnabled:YES];
[web loadHTMLString:html
baseURL:[NSURL fileURLWithPath:
[NSString stringWithFormat:#"%#/htdocs/",
[[NSBundle mainBundle] bundlePath]]]];
- (void)dealloc {
//---release the memory used by the property---
[page release];
[web setDelegate:nil];
[web stopLoading];
[web release];
[orientation release];
[page release];
[super dealloc];
}
The question is how I can load them smoothly in the backround on the UIScrollView and only fire the javascripts once the panel bounces into the viewport, I plan to display more than 60 webviews with its html pages?
You need to recycle your views. Creating a bunch of views when only one at a time will ever be visible is not a good strategy. Apple has a few lengthy video tutorials on the subject. The basic idea is to use at most 3 or 4 views (UiWebViews). As you swipe and the view bounds become visible you initialize it. When it's no longer visible you destroy it. The Wall Street Journal iPad & iPhone app uses a similar pattern.
Here's link to a few resources & tutorial:
Are there any good UIScrollView Tutorials on the net?
Related
I've tried using similar answers to my problem that i've encounter. I'm new to Xcode and I have one controller that contains a uiwebview that loads my website. i have coding to detect if a certain link is pressed. The issue i'm running into is when a certain link is pressed, I need my second controller to act as a pop up and display a different webpage. Everything works and that link is pressed my NSLog says that it in the second controller but the display is black. Here code that i have in the main view controller that loads my webpage:
- (void)viewDidLoad {
[super viewDidLoad];
self.webViewer.delegate=self;
NSString *fullURL = #"https://www.example.com?first_run=true";
NSURL *url = [NSURL URLWithString:fullURL];
//NSLog(#"first page loaded=%#",url);
NSURLRequest *requestObj = [NSURLRequest requestWithURL: url];
[_webViewer loadRequest:requestObj];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
self.webViewer.delegate=self;
NSURL *strurl = request.URL;
NSString *urlString = strurl.relativeString;
NSLog(#"link clicked=%#",urlString);
if(//not the right link do this){
}
else{//is the right link do this
NSLog(#"found");
self.popup = [[PopUpController alloc] init];
[self.popup showInView:self.view animated:YES];
return NO;
}
}
#end
then for my second controller it's supposed to load a different webpage but just get a black screen:
- (void)viewDidLoad {
self.popupView.delegate=self;
NSString *popupURL = #"https://www.example2.com";
NSURL *url_popup = [NSURL URLWithString:popupURL];
NSLog(#"popup page loaded=%#",url_popup);
NSURLRequest *requestObj2 = [NSURLRequest requestWithURL: url_popup];
[_popupView loadRequest:requestObj2];
}
- (void)showAnimate{
self.view.transform = CGAffineTransformMakeScale(1.3, 1.3);
self.view.alpha = 0;
[UIView animateWithDuration:.25 animations:^{
self.view.alpha = 1;
self.view.transform = CGAffineTransformMakeScale(1, 1);
}];
}
- (void)showInView:(UIView *)aView animated:(BOOL)animated{
CGFloat x = self.view.center.x - (aView.frame.size.width);
CGFloat y = self.view.center.y - (aView.frame.size.height);
[aView setFrame:CGRectMake(x,y,aView.bounds.size.width,aView.bounds.size.height)];
[aView addSubview:self.view];
if (animated) {
[self showAnimate];
}
}
Any help would be extremely appreciated. I've tried for 3 weeks searching and trying different solutions found on this forum but they all didn't work for mine. Not sure if I'm just not instantiating my controllers right or some piece of code that I'm missing. Thank you for your time in advanced.
This line:
self.popup = [[PopUpController alloc] init];
simply creates an instance of PopUpController but doesn't load any of you UI elements with it.
Assuming you have designed PopUpController in storyboards, give it an Identifier such as "MyPopUp"
and then use:
// storyboard reference
UIStoryboard *storyboard = [UIStoryboard storyboardWithName: #"Main" bundle:[NSBundle mainBundle]];
// instantiate PopUpController from the storyboard
self.popup = (PopUpController *)[storyboard instantiateViewControllerWithIdentifier:#"MyPopUp"];
// show it
[self presentViewController:self.popup animated:YES completion:nil];
I use UIWebView to load a html page which contains only one SVG image file with below code:
self.webView = [UIWebView new];
self.webView.opaque = YES;
self.webView.frame = self.view.frame;
[_webView setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
self.webView.dataDetectorTypes = UIDataDetectorTypeAll;
self.webView.userInteractionEnabled = YES;
_webView.scalesPageToFit = YES;
self.webView.delegate=self;
[self.view addSubview:self.webView];
and I take the a screen capture as below, I want to make the image file can be auto zoom in to fit the whole window when View is shown, currently I had to double tab the image to make it happen.
and belwo is what I expected:
Found the answer
NSString *jsCommand = [NSString stringWithFormat:#"document.body.style.zoom = 1.5;"];
[_webview stringByEvaluatingJavaScriptFromString:jsCommand];
i am trying to develop a app that will load a iAd like popup view that will show a webview with a image in it. what i mean is that it will load a JPG or PNG url in it. i successfully created that popup but webview in not showing image in it can anyone please debug my code. i will post it here.
MainViewController.m
-(IBAction)openPopupWithURL
{
[MTPopupWindow showWindowWithHTMLFile:#"http://URL/ad.php" insideView:self.view];
}
PopUpWindow.h
#import <Foundation/Foundation.h>
#interface MTPopupWindow : NSObject
{
UIView* bgView;
UIView* bigPanelView;
}
+(void)showWindowWithHTMLFile:(NSString*)fileName insideView:(UIView*)view;
#end
PopUpWindow.m
#import "MTPopupWindow.h"
#define kShadeViewTag 1000
#interface MTPopupWindow(Private)
- (id)initWithSuperview:(UIView*)sview andFile:(NSString*)fName;
#end
#implementation MTPopupWindow
/**
* This is the only public method, it opens a popup window and loads the given content
* #param NSString* fileName provide a file name to load a file from the app resources, or a URL to load a web page
* #param UIView* view provide a UIViewController's view here (or other view)
*/
+(void)showWindowWithHTMLFile:(NSString*)fileName insideView:(UIView*)view
{
[[MTPopupWindow alloc] initWithSuperview:view andFile:fileName];
}
/**
* Initializes the class instance, gets a view where the window will pop up in
* and a file name/ URL
*/
- (id)initWithSuperview:(UIView*)sview andFile:(NSString*)fName
{
self = [super init];
if (self) {
// Initialization code here.
bgView = [[[UIView alloc] initWithFrame: sview.bounds] autorelease];
[sview addSubview: bgView];
// proceed with animation after the bgView was added
[self performSelector:#selector(doTransitionWithContentFile:) withObject:fName afterDelay:0.1];
}
return self;
}
/**
* Afrer the window background is added to the UI the window can animate in
* and load the UIWebView
*/
-(void)doTransitionWithContentFile:(NSString*)fName
{
//faux view
UIView* fauxView = [[[UIView alloc] initWithFrame: CGRectMake(10, 10, 200, 200)] autorelease];
[bgView addSubview: fauxView];
//the new panel
bigPanelView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, bgView.frame.size.width, bgView.frame.size.height)] autorelease];
bigPanelView.center = CGPointMake( bgView.frame.size.width/2, bgView.frame.size.height/2);
//add the window background
UIImageView* background = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"popupWindowBack.png"]] autorelease];
background.center = CGPointMake(bigPanelView.frame.size.width/2, bigPanelView.frame.size.height/2);
[bigPanelView addSubview: background];
//add the web view
int webOffset = 10;
UIWebView* web = [[[UIWebView alloc] initWithFrame:CGRectInset(background.frame, webOffset, webOffset)] autorelease];
web.backgroundColor = [UIColor clearColor];
if ([fName hasPrefix:#"http"]) {
//load a web page
web.scalesPageToFit = YES;
[web loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: fName]]];
} else {
//load a local file
NSError* error = nil;
NSString* fileContents = [NSString stringWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fName] encoding:NSUTF8StringEncoding error: &error];
if (error!=NULL) {
NSLog(#"error loading %#: %#", fName, [error localizedDescription]);
} else {
[web loadHTMLString: fileContents baseURL:[NSURL URLWithString:#"file://"]];
}
}
[bigPanelView addSubview: web];
//add the close button
int closeBtnOffset = 10;
UIImage* closeBtnImg = [UIImage imageNamed:#"popupCloseBtn.png"];
UIButton* closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[closeBtn setImage:closeBtnImg forState:UIControlStateNormal];
[closeBtn setFrame:CGRectMake( background.frame.origin.x + background.frame.size.width - closeBtnImg.size.width - closeBtnOffset,
background.frame.origin.y ,
closeBtnImg.size.width + closeBtnOffset,
closeBtnImg.size.height + closeBtnOffset)];
[closeBtn addTarget:self action:#selector(closePopupWindow) forControlEvents:UIControlEventTouchUpInside];
[bigPanelView addSubview: closeBtn];
//animation options
UIViewAnimationOptions options = UIViewAnimationOptionTransitionFlipFromRight |
UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionBeginFromCurrentState;
//run the animation
[UIView transitionFromView:fauxView toView:bigPanelView duration:0.5 options:options completion: ^(BOOL finished) {
//dim the contents behind the popup window
UIView* shadeView = [[[UIView alloc] initWithFrame:bigPanelView.frame] autorelease];
shadeView.backgroundColor = [UIColor blackColor];
shadeView.alpha = 0.3;
shadeView.tag = kShadeViewTag;
[bigPanelView addSubview: shadeView];
[bigPanelView sendSubviewToBack: shadeView];
}];
}
/**
* Removes the window background and calls the animation of the window
*/
-(void)closePopupWindow
{
//remove the shade
[[bigPanelView viewWithTag: kShadeViewTag] removeFromSuperview];
[self performSelector:#selector(closePopupWindowAnimate) withObject:nil afterDelay:0.1];
}
/**
* Animates the window and when done removes all views from the view hierarchy
* since they are all only retained by their superview this also deallocates them
* finally deallocate the class instance
*/
-(void)closePopupWindowAnimate
{
//faux view
__block UIView* fauxView = [[UIView alloc] initWithFrame: CGRectMake(10, 10, 200, 200)];
[bgView addSubview: fauxView];
//run the animation
UIViewAnimationOptions options = UIViewAnimationOptionTransitionFlipFromLeft |
UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionBeginFromCurrentState;
//hold to the bigPanelView, because it'll be removed during the animation
[bigPanelView retain];
[UIView transitionFromView:bigPanelView toView:fauxView duration:0.5 options:options completion:^(BOOL finished) {
//when popup is closed, remove all the views
for (UIView* child in bigPanelView.subviews) {
[child removeFromSuperview];
}
for (UIView* child in bgView.subviews) {
[child removeFromSuperview];
}
[bigPanelView release];
[bgView removeFromSuperview];
[self release];
}];
}
#end
and this is output
My ad.php
<?php
include("../includes/opencon.php");
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Mobile Ad</title>
</head>
<body>
<?php
$setting = mysql_fetch_object(mysql_query("select * from my_settings where id=1"));
?>
<img src="http://url.com/media/<?=$setting->image?>" style="width:100%; height:auto;"/>
</body>
</html>
Please provide additional information, whether this happens when loading a local or remote image. I think for local images you could adjust your base url of the webview.
If you want to load local images, please provide the base URL of the App bundle:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[webView loadHTMLString:htmlString baseURL:baseURL];
So instead of:
[web loadHTMLString: fileContents baseURL:[NSURL URLWithString:#"file://"]];
use the app bundle url:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[web loadHTMLString: fileContents baseURL:baseURL];
For further debugging, you could set the webview's delegate to your view and check, what the webview's content looks like:
web.delegate = self;
To now use the delegate implement the following:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *html = [webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.outerHTML"];
NSLog(#"%#",html);
}
Alternatively, go to Safari->Develop->iPhone Simulator/iOS Device->Webview
And take a look at the source code in the debug console of safari.
In my iPhone app i am getting around 300 images url from web server, i need to display all the images on UIScrollView in 3x3 row and column format. Once i get all the url from web server, convert them into UIImage and display image. Everything works fine, however after loading 200 images app crashes, I have read a lot its some thing memory kind of issue with UIScrollView, so how can we work on this. I have read apple documentation also, however its not clear.
here is the solution, i've implemented it in my app. Here is my code
sv_port is my scrollView.
Download SDWebImageDownloader class files and import it in your project. Add relevant framework in your project. like imageIO.framework, QuartzCore
Now, in your .m add
#import "UIImageView+WebCache.h"
#import "SDImageCache.h"
#import "SDWebImageCompat.h"
//function for adding image in your scrollview
-(void)populateImages
{
int x=6,y=6;
for(UIView * view in sv_port.subviews){
if([view isKindOfClass:[UIImageView class]])
{
[view removeFromSuperview]; view = nil;
}
if([view isKindOfClass:[UIButton class]])
{
[view removeFromSuperview]; view = nil;
}
}
for(int p=0;p<[Manufacturer count];p++)
{
UIButton *btnImageButton = [UIButton buttonWithType:UIButtonTypeCustom];
btnImageButton.frame = CGRectMake(x,y,70,70);
[btnImageButton setTag:p];
[btnImageButton addTarget:self action:#selector(nextview:) forControlEvents:UIControlEventTouchUpInside];
UIActivityIndicatorView *spinny = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinny.frame=CGRectMake(btnImageButton.frame.origin.x+25,btnImageButton.frame.origin.y+25, 20, 20);
spinny.backgroundColor=[UIColor clearColor];
[spinny startAnimating];
[sv_port setScrollEnabled:YES];
[sv_port addSubview:spinny];
UIImageView *asyncImageView = [[[UIImageView alloc] initWithFrame:btnImageButton.frame] autorelease];
asyncImageView.backgroundColor=[UIColor clearColor];
CALayer *layer;
layer = asyncImageView.layer;
layer.borderWidth = 0.5;
layer.cornerRadius = 5;
layer.masksToBounds = YES;
[asyncImageView setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#", yourUrl] ] placeholderImage:[UIImage imageNamed:nil] options:0 andResize:CGSizeMake(btnImageButton.frame.size.width,btnImageButton.frame.size.height) withContentMode:UIViewContentModeScaleAspectFit];
asyncImageView.contentMode = UIViewContentModeScaleAspectFit;
[sv_port addSubview:btnImageButton];
[sv_port addSubview:asyncImageView];
int imgCntPort=0;
imgCntPort=(sv_port.frame.size.width/(asyncImageView.frame.size.width));
////NSLog(#"imgport %d",imgCntPort);
if((p+1)%imgCntPort==0)
{
x=5;
y=y+80;
}
else
{
x=x+80;
}
}
sv_port.contentSize = CGSizeMake(0, y);
glob_x =0; glob_y =y;
}
hope it helps..
-(void)scrollimage
{
[AsyncImageLoader sharedLoader].cache = nil;
NSArray *viewsToRemove = [_scrollview_gallary subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
CGFloat width =_scrollview_gallary.bounds.size.width;
_scrollview_gallary.contentSize=CGSizeMake(width*[tmpArr count]-1, 360);
[_scrollview_gallary setShowsHorizontalScrollIndicator:NO];
[_scrollview_gallary setShowsVerticalScrollIndicator:NO];
int x=0;
int y=0;
for (int i=0; i<[tmpArr count]; i++)
{
AsyncImageView *imgv;
imgv =[[AsyncImageView alloc] initWithFrame:CGRectMake(x,y,width,360)];
imgv.contentMode = UIViewContentModeScaleToFill;
imgv.activityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
NSString *strimag=[NSString stringWithFormat:#"%#",[tmpArr objectAtIndex:i]];
imgv.imageURL=[NSURL URLWithString:[tmpArr objectAtIndex:i]];
[_scrollview_gallary addSubview:imgv];
x=x+width;
}
_pageview.numberOfPages=([tmpArr count]);
_scrollview_gallary.pagingEnabled = YES;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
int page;
CGFloat pageWidth = _scrollview_gallary.frame.size.width;
page = floor((_scrollview_gallary.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageview.currentPage = page;
}
I am getting an impression that you are downloading all 300 images together at once and keeping in memory and adding to the UIScrollView..
There is a technique called LazyLoad, means don't load all the images at once, load it in queue..
You can refer to this link http://lazyloadinginuiscrollviewiniphone.blogspot.in/2011/12/iphone-uiscrollview-lazy-image-loading.html it might help you to achieve things smoothly..
All the best.
If you want to display images in Gridview format then you can use PSTCollectionView. And for the lazy loading of the image you can use SDWebImage.
Are you releasing the elements that are not visible anymore? UITableView does that (that's why Sandro suggested it).
Your implementation of the UIScrollView should do that.
You can also consider UICollectionView or GMGridView for grid implementations that take this into consideration.
I do not see why what you're doing is a problem. May be you've got memory issue. In that case simulator and device result should be different.
If this is a memory problem, you should use a tableview instead of a scroll view. Put 3 images in each table view cell, and that should do the trick.
Cdt
I found a great tutorial online that shows you how to page through different pages, well kind of. Here is the link to the tutorial here: http://www.iosdevnotes.com/2011/03/uiscrollview-paging/
The question I have is, how can I tweak his code so that instead of loading "colors" it loads different independent views or view controller files I create? Like I see it loads it's information from the NSArray, but how do you code it so that you include views or view controllers inside that array, and do you have to add anything else special to make this happen?
Here is the code I'm referring to from his tutorial which is in the implementation file:
- (void)viewDidLoad {
[super viewDidLoad];
pageControlBeingUsed = NO;
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[self.scrollView addSubview:subview];
[subview release];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * colors.count, self.scrollView.frame.size.height);
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = colors.count;
}
As a quick thought, you could try creating an array or similar of views instead of colours.
Then in the for loop get the views out of the array and use this in the addSubView: message.
This would work for a few views but would not scale well.
I do something similar to this but with UIImageViews to be able to scroll a few images.