Add subview (rows) fast to UIScrollView while scrolling - iphone

I have UIScrollView with a lot of rows (~100) and I implemented dequeueReusableRow method for fast allocating and adding my subviews (rows). Everything work fine, but if I scroll very fast with decelerate some view don't added to scrollView on time only later.
- (UIView *)dequeueReusableRow
{
UIView *view = [reusableRows anyObject];
if(view)
{
[[view retain] autorelease];
[reusableRows removeObject:view];
}else{
view = [[UIView alloc] init....
}
return view;
}
- (void)addVisibleRows
{
UIView *row = [self dequeueReusableRow];
row.frame = ....
[scrollView addSubview:row]
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self addVisibleRows];
[self removeInvisibleRows];
}
Please, don't propose me use UITableView because structure of accordion looks like:
section
- section
-- section
--- row
- section
section
- row

dequeueReusableRow is not part of UIScrollViewController, so I assume dequeueReusableRow is something you implemented yourself. If so, can you show that code? It is likely where we can help with any inefficiencies.
Also, if your scrollview contains rows, why not just use a UITableView which will do much of the work for you? I know you asked not to propose it - can you explain why you need to use a ScrollView so we can help you better?

It's very hard to tell from that code snippet. Some more details would be appreciated.
One minor suggestion in the meantime: Call removeInvisibleRows before addVisibleRows

Related

iOS: Removing one view from superview causes another to get removed?

This is a very odd problem. I have 5 subviews added to a UIViewController. One of them needs to be removed, but when I do this, one of the remaining 4 subviews is also removed. This necessitates that I re-add it using addSubview. The two views in question are not related in any way.
Is this a known iOS SDK bug? It happens for sure running on the simulator with iOS 6.1.
Thanks.
Here, In Your Question not mention That which Method you use for remove subView so,I give you simple suggestion for remove subView.
Give Tag of Each subView such like,
self.subView1.tag = 1;
self.subView2.tag = 2;
.
.
.
.
self.subViewN.tag = N;
And You can access(Remove) any subView base on its Tag, such like
[[self.view viewWithTag:1] removeFromSuperview];
This tips might helpful for you.
You can remove single subview using the following code.
[subview_Name removeFromSuperview];
if you want to remove all subviews form the view then use this.
NSArray *subViewArray = [self.view subviews];
for (id obj in subViewArray)
{
[obj removeFromSuperview];
}
if you want to remove all subview of particular class then use this.
NSArray *subViewArray = [self.view subviews];
for (id obj in subViewArray)
{
if([obj isKindOfClass:[classname class]])
{
[obj removeFromSuperview];
}
}
example : if you want to remove subview of UIImageView class then replace if condition with this.
[obj isKindOfClass:[UIImageView class]]

Creating a custom view in Interfacebuilder and using that in code

I have made a view in the application that shows a UITableView. It will be full of results (obviously) almost all the time, however when it does not have any results I want to show another view that inform the user about how he/she could populate the table.
I want to design that view in the interfacebuilder. I will have to check in the code whether the datasource is empty or not to toggle between the two different nibs. How do I instantiate and configure a view made in Interfacebuilder?
The easies way to do this is by adding the view in xib normally and make it visible
Design your both views, the table view and the other view, give the tableView a tag of 111 for example and give the otherview another tag 222 for example
Now in viewDidLoad
Get both the views
UIView *noDataView = [self.view viewWithTag:222];
UITableView *tableView = [self.view viewWithTag:111];
//Hide both of them or only the noDataView until you know if you have data from the dataSource or not
Check for your data source
//hasElements do you have any element to show?
if(hasElements)
{
noDatView.hidden = YES;
tableView.hidden = NO;
}
else
{
noDatView.hidden = NO;
tableView.hidden = YES;
}
You can load nib file based on condition.You can write category as follows:
self.view = (UIView *)[self loadNib:#"SecondView" inPlaceholder:self.view];
- (UIView *)viewFromNib:(NSString *)nibName
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
for (id view in xib) { // have to iterate; index varies
if ([view isKindOfClass:[UIView class]]) return view;
}
return nil;
}
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
UIView *nibView = [self viewFromNib:nibName];
[nibView setFrame:placeholder.frame];
self.view = nibView;
//[self.view insertSubview:nibView aboveSubview:placeholder];
//[placeholder removeFromSuperview];
return nibView;
}
The other answers give you possible technical solutions but I would propose that if you are using the standard Apple design guidelines, you probably don't even need to worry about it. For instance, somewhere on your screen you should have a bar button item with the identifier "Add" (which shows the plus icon). Then rather than giving a long (often poorly localised) description of how to add items, just have a header for an empty section which says "No items" replacing items with whatever pluralised noun is appropriate for your table's items. For example, for an Archery related app I am working on:
Notice how the Edit button is currently disabled too, thus no explanation is needed as the only thing they can do at this point is tap the Add button (screenshots on the Appstore will have shown them what they can expect to see after this point).

Move buttons down in UIAlertView

I have an app which displays in landscape mode and I've overwritten the height of a UIAlertView with the following code:
- (void) willPresentAlertView:(UIAlertView *)alertView
{
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGRect frame = [alertView frame];
alertView.frame = CGRectMake(frame.origin.x, 0, frame.size.width, screenBounds.size.width);
}
This almost works. The UIAlertView is taller, however, the buttons in the alertview don't get pushed down.
Does anyone know how I can push the buttons in a UIAlertView down when I change the alert view's height?
I think it is more elegant and less risky to replace UIAlertView with some independent AlertView instead of messing around with it.With independent I mean not inheriting form UIAlertView.
TSAlertView is such a replacement.
http://dl.dropbox.com/u/47535/TSAlertView/1-thumb.png
http://dl.dropbox.com/u/47535/TSAlertView/3-thumb.png
http://dl.dropbox.com/u/47535/TSAlertView/2-thumb.png
I'm guessing that you'll probably have to subclass / manipulate the UIAlertView class to achieve that, which can be risky as Apple can change their implementations so wrap your code in appropriate checks.
A start would be to do:
NSLog(#"UIAlertView subviews: %#",alertView.subviews);
That'll print some output for the various elements making up the UIAlertView, you will probably see a few UIButtons in there which you can then manipulate by using something like:
UIButton* button1 = (UIButton*)[alertView.subviews objectAtIndex:N];
[button1 setFrame:CGRect(button1.frame.origin.x, button1.frame.origin.y+10, button1.frame.size.width, button1.frame.size.height)]
A sensible check would be to confirm that the objectAtIndex is the correct type of element before you perform operations on it, this isn't foolproof however as Apple could add more UIButtons or something..
if ([[alertView.subviews objectAtIndex:N] isKindOfClass:[UIButton class]]) {
...
}
Depending on your situation you may also want to iterate over the subviews array and move all UIButtons down rather than just hardcoding in some specific objectsAtIndex but that's a whole other answer. :-)
This link should help. In my opinion, I would not try messing around with the view hierarchies. This can lead to rejection from the App Store. Either build a new AlertView from scratch, or leave it as it is.
NSArray *subViewArray=[alertView subviews];
for (NSUInteger ind=0 ; ind < [subViewArray count]; ind++) {
if ([[alertView.subviews objectAtIndex:ind] isKindOfClass:[UIButton class]]) {
UIButton* button1 = (UIButton*)[alertView.subviews objectAtIndex:ind];
[button1 setFrame:CGRectMake(button1.frame.origin.x, button1.frame.origin.y+100, button1.frame.size.width, button1.frame.size.height)];
NSLog(#"button1.frame.origin.y=[%1.0f]",button1.frame.origin.y);
}
}

Iphone : How to scroll to the 1st cell of the 2nd section, letting the header of the 1st section visible

I have an UITableView with rows and sections.
I would like to scroll to the first item of the second section, letting the header of the first section visible. Like if I had manually scrolled the list until reaching that state.
---- TOP OF SCREEN ----
Header of first section
Header of the second section
cell 1
cell 2
cell 3
Header of the third section
cell 1
cell 2
...
scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]
does not do the job, it hides the header of the first section.
We're moving on. I found this method based on Kevin's idea. To be able to set animated to YES, I catch the end of animation using a delegate method of UIScrollView. It works. But any solution that would help not doing 2 animations would be greatly appreciated. Any idea about how to do this ?
- (IBAction) scrollToToday:(BOOL)animate {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:animate];
if (animate == NO) [self showFirstHeaderLine:NO];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self showFirstHeaderLine:YES];
}
- (void) showFirstHeaderLine:(BOOL)animate {
CGRect headerRect = [self.tableView rectForHeaderInSection:1];
CGPoint scrollPoint = headerRect.origin;
scrollPoint.y -= headerRect.size.height;
[self.tableView setContentOffset:scrollPoint animated:animate];
}
Dude to this code, the process when animated is set to YES should loop infinitely beetween scrollViewDidEndScrollingAnimation and showFirstHeaderLine... It loops, yes, but only once... Any idea about why ?
You can grab the rect for the row you want, then subtract the height of the header of the previous section and scroll to that point. Something like the following (untested) should work:
CGRect rowRect = [table rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
CGRect headerRect = [table rectForHeaderInSection:0];
rowRect.origin.y -= headerRect.size.height;
rowRect.size.height += headerRect.size.height;
[table scrollRectToVisible:rowRect animated:YES]; // UITableView is a subclass of UIScrollView
I tried your code, and it works!!
For the loop question, since you are setting a offset(SetContentOffset), it has nothing to do with the scroll. It is will not call scrollView delegate. SO the scrollViewDidEndScrollingAnimation will be called only once, which has been called from scrollToRowAtIndexPath.

dynamicly loading content into scrollview while scrolling

I'm facing a problem with my scrollview (horizontal, paging enabled, can be 50 pages or more)
what i want to do is, load content onto the scrollView's content-View while the user is scrolling horizontally (because initially loading all the content takes way to long).
what i do is:
everytime a scrollViewDidScroll happens i check if the loading of additional content is necessary. in case it is, a new "page" is being created and added onto the scrollViews contentview at the correct position.
the problem is:
that while it preloads a page, the "scrolling-movement" gets disturbed. the screen doesn't exactly flicker, but the smoothness of the scrolling animation suffers heavily.
in code that is:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self preLoadPageNumber:self.currentPageIndex + 2];}
-(void)preLoadPageNumber:(int)pageNumber{
NSMutableArray *tmpMovies;
VoDPage *pageView;
if(pageNumber < [self getAmountOfPages])
{
pageView = [pageViewControllers objectAtIndex:pageNumber];
if((NSNull*)pageView == [NSNull null])
{
tmpMovies = [[NSMutableArray alloc] initWithCapacity:6];
for(int o = ((pageNumber) * 6); o < ((pageNumber+1)*6); ++o)
{
#try {
[tmpMovies addObject:[movies objectAtIndex:o]];
}
#catch (NSException * e) {
break;
}
}
pageView = [[VoDPage alloc] initWithContent:tmpMovies andNavCtrl:navCtrl];
pageView.view.frame = CGRectMake(((pageNumber) * 320)-8 , 0, 320, 332);
if(editModeEnabled)
[pageView enableEditMode];
[scrollView addSubview:pageView.view];
[pageViewControllers replaceObjectAtIndex:pageNumber withObject:pageView];
}
}
}
probably I have to make some design changes here. I'm sure some of you have faced similar problems in the past.
Any help / tipps or links are greatly appreciated
thanks in advance for your help
sam
Ok i somewhat figured out a workaround that does the trick.
i changed the preLoadPageNumber- Method to look like this:
-(void)preLoadPageNumber:(NSNumber*)param
simply because i call a helper method from within ScrollViewDidScroll and said helperMethod looks like this:
-(void)performPreLoadAfterDelay
{
[self performSelector:#selector(preLoadPageNumber:) withObject:[NSNumber numberWithInt:self.currentPageIndex +1] afterDelay:0.001];
}
Calling performSelector from within the ScrollViewDidScroll Method doesn't work when scrolling very fast ("blank" pages appear)..
I don't really know why, but the trick with the helperMethod worked.
Annimation is smooth now and the "blank-page"-problem dissappeared.