Adding views lazily in UIScrollView slowing down App - iphone

I am trying to implement a view with a mainScrollView. Inside that mainScrollView I have 10 UIView, each one is having subScrollView.Now, I want to add many (around 150 to 200) views(imageview,label,textview,uiview,webview,etc) inside each subScrollView.
something like this:
Timeline ww2 and
History of Jazz
I have tried following:
Method 1:
Lazy loading for each subScrollView to add views.
code for this:
-(void)loadInnerScrollViewLazily:(int)tag pageNo:(int)pageNo{
/// this array will contain all the views for subScrollView
NSMutableArray *array=[NSMutableArray array];
array=[self.tlScrollContentArray objectAtIndex:tag];
if (pageNo < 0)
return;
if (pageNo >= [array count])
return;
UIView *controller = [array objectAtIndex:pageNo];
CGRect frame = [[array objectAtIndex:0] frame];
frame.origin.x = 350;
frame.origin.y = 0;
controller.frame = frame;
for (UIView* view in [mainScrollView subviews]) {
for (UIScrollView* scroll in [view subviews]) {
if ([scroll isKindOfClass:[UIScrollView class]]) {
if (scroll.tag == tag+11) {
[scroll addSubview:controller]; /// adds view in particular subScrollView
}
}
}
}
}
This method is called in scrollViewDidScroll method, which checks perticular scrollView and removes view which is out of screen and then it will add views depending upon the condition.
This code is slowing down app as scrolling rate increases.
Method 2:
With the reference of this tutorial:
Add three UIView(each with subScrollView) in to mainScrollView. Updating content of subScrollView depending upon the condition.
But this solution is also slowing down my app as it is updating subScrollView every time it appears.
Is there any library or code available to manage this.
Any help will be appreciated.
Thanks in Advance!

I think your code has many, many errors, and you would need to post your complete source to be helped. It's unclear if you are ever removing images from your scrollview. If not, that is your problem.
Look at Apple's sample PhotoScroller code: http://developer.apple.com/library/ios/#samplecode/PhotoScroller/Introduction/Intro.html
This dequeues and manages the ImageViews on the scrollview... I implemented something similar based on this.

Related

iOS : How to do proper paging in UIScrollView?

I've a UIScrollView of size (320,160). I'm adding some UIImageView into it, which are of size (213,160). The first UImageView starting from 54 (x) and so on, I've added a space of 5.0 in between each UIImageView. I've also enabled pagingEnable in IB & in coding. What my problem is its not properly working as per its property! When I scroll it should show me UIImageViews in each single page instead it showing me something like see screenshot I want output something like this see output screenshot
Where I'm doing wrong? I also having function of< (previous) & > (next) there to show images. I've asked one question yesterday which was I accepted however my requirement is little change and it won't become my solution. see my question.
Is there any special property that I've to set, or some logic I should implement? All examples I've checked and tried but I find that my requirement is some special. Point me! Thanks.
EDITED:
- (void) setImages
{
CGFloat contentOffset = 0.0f;
for (int i=0; i<[arrImgUrls count]; i++)
{
CGRect imageViewFrame = CGRectMake(contentOffset, 0.0f, 213, scrollImages.frame.size.height);
AsyncImageView *asyncImageView = [[AsyncImageView alloc] initWithFrame:imageViewFrame];
[asyncImageView.layer setMasksToBounds:YES];
NSString *urlImage = [arrImgUrls objectAtIndex:i];
NSURL *url = [NSURL URLWithString:urlImage];
[asyncImageView loadImageFromURL:url];
[scrollImages addSubview:asyncImageView];
contentOffset += asyncImageView.frame.size.width+increment;
[asyncImageView release];
}
scrollImages.contentSize = CGSizeMake([arrImgUrls count] * scrollImages.frame.size.width, scrollImages.frame.size.height);
scrollImages.pagingEnabled = YES;
scrollImages.clipsToBounds = NO;
}
-(IBAction)prevImage:(id)sender
{
_currentImage--;
[btnNext setEnabled:YES];
if (_currentImage==0)
{
[btnPrev setEnabled:NO];
[scrollImages setContentOffset:CGPointMake((_currentImage*imageWidth), 0) animated:YES];
return;
}
NSLog(#"previous:mult %d inc %d current %d",_currentImage*imageWidth,increment*_currentImage,_currentImage);
int nextImage=_currentImage+2;
[scrollImages setContentOffset:CGPointMake((((_currentImage*imageWidth)-(increment*_currentImage)))+(nextImage*increment), 0) animated:YES];
}
-(IBAction)nextImage:(id)sender
{
_currentImage++;
NSLog(#"next:mult %d inc %d current %d",_currentImage*imageWidth,increment*_currentImage,_currentImage);
[scrollImages setContentOffset:CGPointMake((_currentImage*imageWidth)+(increment*_currentImage), 0) animated:YES];
[btnPrev setEnabled:YES];
if (_imageCount-1 == _currentImage)
{
[btnNext setEnabled:NO];
}
}
Paging scroll views alway page multiples of their frame size. So in your example paging is always +320.
This behavior is good if you have content portions matching the frame of the scroll view.
What you have to do, is giving your scroll view a width of 213 and set its clipsToBounds property to NO.
After that your scroll view pages exactly how you want and you see what's left and right outside the frame.
Additionally you have to do a trick to make this left and right area delegate touches to the scroll view.
It's greatly explained in this answer.
You are forgetting to set scrollview content size.
make sure that you have set the content size to fit N number of images.
if you want to scrollview to scroll for 10 images with an image on each page
set scrollView contentSize as
[scrollView setContentSize:CGSizeMake(320 * 10,200)];

iOS iPhone movie list horizontal slide show with title

I'm fairly new to iOS development and I'm currently working on my thesis about this topic. My assignment is to build an app for a movie theater. The design has a horizontal slider containing the latest movie posters with the title, and I'm wondering how I should approach this design.
Screenshot
http://cl.ly/2S1b381S2v1x2y0U1q2H
There is an array with the movie poster image and the title, and I wan't them to show like the screenshot and being able to scroll horizontally between them.
I've looked around but I'm getting very confused by different approaches that are not 100% the same as my problem.
The nav bar and tab bar are just placeholders, I know how to implement those.
I'm hoping someone can help me with getting started with this design, how I should approach this.
Thanks in advance,
Lars
You can download and use the demo application at the following url as reference
https://developer.apple.com/library/ios/#samplecode/Scrolling/Introduction/Intro.html
By increasing the size of the UIScrollView will be showing part the contents towards left and right.
In the demo there are showing images, instead of that create a UIView which hold image and title and replace should work.
- (void)loadScrollViewWithPage:(int)page scrollingView:(UIScrollView*)scrollView
{
//NSLog(#"page %d",page);
if (page < 0) return;
if (page >= numberOfPages) return;
// replace the placeholder if necessary
IconsViewController *controller;
if (scrollView == iconsScrollView)
{
controller = [viewControllers objectAtIndex:page];
}
if ((NSNull *)controller == [NSNull null])
{
controller = [[IconsViewController alloc] init];
controller.pageIndex = page;
if (scrollView == iconsScrollView)
{
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
You should use a UIScrollView with paging enabled.
And then take a look at this approach: https://stackoverflow.com/a/3018776/696440

UIAccessibilityContainer in a table view

I'm trying to put a custom view inside a UITableViewCell which of course lives within a UITableView. I want to make this custom view accessible so I need to make it a UIAccessibilityContainer (since it contains several visual elements that aren't implemented as their own UIViews).
When I do this, the location of the elements get all messed up whenever the table scrolls. While paging through the elements using VoiceOver, it will automatically scroll the table to attempt to center the selected element on screen, but then the outline of where VoiceOver thinks the element is no longer lines up with where it is visually.
Note in the screenshot that the inspector says "Row 4, element 2" but the highlighted area is some random place in Row 7 since that happens to be where Row 4 was before it auto-scrolled the table.
My thought is that I might have to use UIAccessibilityPostNotification() to post a layout change when the table view scrolls, but I don't have to do that when I don't use a UIAccessibilityContainer and it feels like I shouldn't have to do it and that the system should be handling this for me - but the fact that UIAccessibilityElement needs to have it's accessibilityFrame set in screen coordinates does seem to throw a wrinkle into things. (Bonus question: Why the heck is the API designed that way? Why not define the frame relative to the element's container or something like that? Arg.)
Here's the custom view's implementation just in case there's something in here which is causing the problem. For the full project (Xcode 4), click here.
#implementation CellView
#synthesize row=_row;
- (void)dealloc
{
[_accessibleElements release];
[super dealloc];
}
- (void)setRow:(NSInteger)newRow
{
_row = newRow;
[_accessibleElements release];
_accessibleElements = [[NSMutableArray arrayWithCapacity:0] retain];
for (NSInteger i=0; i<=_row; i++) {
UIAccessibilityElement *element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
element.accessibilityValue = [NSString stringWithFormat:#"Row %d, element %d", _row, i];
[_accessibleElements addObject:element];
[element release];
}
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[[UIColor lightGrayColor] setFill];
UIRectFill(self.bounds);
[[UIColor blackColor] setFill];
NSString *info = [NSString stringWithFormat:#"Row: %d", _row];
[info drawAtPoint:CGPointZero withFont:[UIFont systemFontOfSize:12]];
[[[UIColor whiteColor] colorWithAlphaComponent:0.5] setFill];
NSInteger x=0, y=0;
for (NSInteger i=0; i<=_row; i++) {
CGRect rect = CGRectMake(12+x, 22+y, 30, 30);
UIAccessibilityElement *element = [_accessibleElements objectAtIndex:i];
element.accessibilityFrame = [self.window convertRect:[self convertRect:rect toView:self.window] toWindow:nil];
UIRectFill(rect);
x += 44;
if (x >= 300) {
x = 0;
y += 37;
}
}
}
- (BOOL)isAccessibilityElement
{
return NO;
}
- (NSInteger)accessibilityElementCount
{
return [_accessibleElements count];
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
return [_accessibleElements objectAtIndex:index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
return [_accessibleElements indexOfObject:element];
}
#end
Edit: I should note that I've tried variations that update the element's accessibilityFrame in -indexOfAccessibilityElement: and -accessibilityElementAtIndex: with the idea that VoiceOver will request the element somehow whenever it needs it and that'd be a nice time to update things. However that doesn't seem to work, either. I was kind of hoping maybe VoiceOver would automatically request things to redraw, but that also doesn't seem to work. (The idea of putting the location setting code in -drawRect: comes from something I remember seeing at WWDC about this, but it was unclear to me if that was "best practice" or just happened to be convenient.)
I've solved the problem you described by adding some side effects to the accessibility methods and with collaboration from the table scroll delegates. Inside the drawRect method I calculate the local coordinates of the rectangle, so I don't need to convert the coordinates there, simply calculate them with regards to the cell's top left corner.
Then, I modified the accessor to update the frame with a side effect like this (note the y resetting):
- (id)accessibilityElementAtIndex:(NSInteger)index
{
UIAccessibilityElement *element = [accesible_items_ get:index];
CGRect rect = element.accessibilityFrame;
rect.origin.y = 0;
element.accessibilityFrame = [self.window
convertRect:rect fromView:self];
return element;
}
While this works fine for the initial view, you still get the displaced frames when the user scrolls, so in your table view controller implement the following scrolling delegate:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// This loop has a side effects, see the cell accesor code.
for (id cell in self.tableView.visibleCells)
for (int f = 0; [cell accessibilityElementAtIndex:f]; f++);
UIAccessibilityPostNotification(
UIAccessibilityLayoutChangedNotification, nil);
NSLog(#"Layout changed after scrollViewDidScroll");
}
Depending on the contents of your table not all cells may respond to the accessibility method, so you could first query each cell with respondsToSelector to avoid sending unexpected messages.
I would also post UIAccessibilityLayoutChangedNotification at the end of the cell setter creating the UIAccessibilityElement objects, or you will get log messages saying that your elements disappeared or could not be found.
These changes make the scroll work when iterating the elements one through one with the rotor, but you may get still odd results if the user scrolls with a triple finger gesture. That's because by default tableViews scroll a screen page at a time, which may not happen to have the same element boundaries as your cells, and the rotor selects a cell half visible. Depending on the scrolling direction and other UI elements, the half visible cell could overlap controls the rotor gets confused with. You need to implement paged scrolling to control avoid this behavior.
Is this problem affecting the usability of the app in VoiceOver mode? When I've played with VoiceOver on both Mac OS and iOS, the highlight boxes (especially in Web views) frequently become unmatched with their onscreen objects. If the app is still usable in VoiceOver, I'd call this a known bug and fix it if somebody complains.
After all, most of the blind people I know aren't looking at the highlight boxes.

iPhone SDK Managing Memory for Multiview app

I have an app that programmatically creates labels and text fields based on the contents of a text file. Whenever the view controller is loaded, it creates text fields and labels that are different each time. My problem is I need to clear out the labels and text fields without releasing the view controller since I need to keep track of the view controller. I tried self.viewController = nil, but I'm pretty sure this results in a memory leak. Is there a way to delete all the subviews of a view?
What Greg meant to say is this:
for (UIView *subview in self.view.subviews) {
[subview removeFromSuperview];
}
This may not work as you would expect though, as Objective C doesn't like it when you modify an array while you're iterating through it in a for loop. A safer choice would be this:
while ([self.view.subviews count] > 0) {
[[self.view.subviews lastObject] removeFromSuperview];
}
If you have a view named view, you should be able to remove all of the subviews from the view using this code:
for (UIView *subview in view) {
[subview removeFromSuperview];
}

UIScrollView. Any thoughts on implementing "infinite" scroll/zoom?

So, UITableView supports essentially "infinite" scrolling. There' may be a limit but that sucker can scroll for a looonnnggg time. I would like to mimic this behavior with a UIScrollView but there are two fundamental impediments:
1) scrollView.contentSize is fixed at creation time.
2) zooming can blow any lazy-loading scheme all to hell since it can cause infinte data explosion.
Have others out there pondered this idea? Yah, I know, we are essentially talking about re-creating Google Maps here. Any insights would be much appreciated.
Cheers,
Doug
I've just finished implementing the infitine scroll for me.
In my Implementation I have UITableViewCell with a scrollView and Navigationbuttons. The scrollView contains x views all with the same width. views are alined horizontally and paging is enabled.
scrollView.clipsToBounds = YES;
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsHorizontalScrollIndicator = NO;
My codelogic is like the following:
In my initialization function I
create all the views (for the scrollview) and
put them into an array and
add them to the scrollView
Then I call a function that calculates in a loop the positions for each view (each time you detect a scroll this function will need to be called too). It always takes the first element of the array and sets the frame to (0,0,...,...), the second with (i*width,0,....,....) and so on. The function beeing called looks like this:
- (void)updateOffsetsOfViews{
int xpos = 0;
for (int i=0; i<[views count]; i++) {
UIImageView *_view = [views objectAtIndex:i];
CGRect aFrame = _view.frame;
aFrame.origin.x = xpos;
aFrame.origin.y = 0.0;
_view.frame = aFrame;
xpos += viewWidth;
}
float center = 0;
if(fmod([views count],2) == 1){
center = viewWidth * ([views count]-1)/2;
}else {
center = viewWidth * [views count]/2;
}
[scrollView setContentOffset:CGPointMake(center, 0)];
lastOffset = center;
}
Then (still in the initialization process) I add an observer
[scrollView addObserver:self forKeyPath:#"contentOffset" options:0 context:nil];
so each time something in the scrollView changes I get the (observeValueForKeyPath)-function called, which looks like this:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
UIImageView *_viewFirst = (UIImageView *)[views objectAtIndex:0];
if ( fmod([scrollView contentOffset].x,viewWidth) == 0.0) {
if ([scrollView contentOffset].x > lastOffset) {
[views removeObjectAtIndex:0];
[views addObject:_viewFirst];
[self updateOffsetsOfViews];
}else if ([scrollView contentOffset].x < lastOffset) {
UIImageView *_viewLast = (UIImageView *)[views lastObject];
[views removeLastObject];
[views insertObject:_viewLast atIndex:0];
[self updateOffsetsOfViews];
}
}
}
And in dealloc or viewDidUnload (depends on how you implement it) don't forget to remove the observer.
[scrollView removeObserver:self forKeyPath:#"contentOffset"];
Hope this helps, you might notice some overhead, but in my implementation I also support like scrolling 5 pages (well... unlimited) at once and autoanimated scrolling etc. so you might see something that could be thrown away.
While it's impossible to have a truly infinite UIScrollView, there are some simple tricks you can use to emulate that behavior.
Handling the fixed contentSize: have some fixed-size view handled by your scroll view, and at launch or instantiation, set the content offset so that you're seeing the middle of the handled view. Then just watch the content offset (using KVO or some other method), and if you near any edge, update the content of the view with a new set of content (offset appropriately) and reset the scroll view's contentOffset property to be back in the middle.
Handling zooming: do something similar, only this time watch the zoom factor on the scroll view. Whenever it gets to a certain point, do some manipulation to whatever data you're presenting so that it appears zoomed, then reset the zoom factor to 1.0. For example, if you're scrolling an image and it gets zoomed to appear twice as large, programmatically apply some kind of transform to make the image twice as large, then reset the scroll view's zoom factor to 1.0. The image will still appear zoomed in, but the scroll view will be able to continue zooming in further as necessary. (Google Maps takes this one step further where it lazy-loads more detailed views as the user zooms - you may or may not choose to implement this.)
The StreetScroller sample project from Apple demonstrates how to perform infinite scrolling in a UIScrollView.
Bear in mind that when the scroll is animated, contentOffset changes many times, not just page by page, but with each step in the animation.
Perhaps setting contentSize to some gigantic value and then moving a limited number of underlying views around to track the view position as in the Tiling sample will do the trick.
To mitigate the possibility of eventually reaching an edge and having to abruptly recenter the view (which cancels any scrolling currently in motion), the view can be recentered when it is stationary, from time to time.
Anyway, that's what I'm about to try.