I have a Nib file containing grouped table view and a cell. The cell is connected to UITableViewController through outlet. In cellForRowAtIndexPath I just return that cell and I can see a single cell in the table view. But when I change the row count of table to 2 and want to show the same cell, then I can see only one, it appears that the second cell is intended to be there, as the lower corners of the visible cell are not rounded, however, it's not there.
If I create a second cell object in nib file, second outlet and return it as second row, the it appears fine. My cell has identifier specified in IB.
Does it mean I can't re-use cell object for more than one row?
In the same way as you would need one instance of a UIButton for each visible button in your view, you will need one instance of your cell for each visible row.
The common pattern to manage this, is to ask the tableview for a previously instantiated cell that is no longer needed (dequeueReusableCellWithIdentifier:), and then return that cell. If the table view does not have any reusable cells, you have to instantiate a new one from your nib file.
There are many examples on this around the web, and you can also find some here at SO, ex in this answer.
I would recommend that you read through Apples TableView Programming Guide, which also contains a section on loading cells from nibs.
UPDATE:
An attempt on explaining the TableView and reuse of cells in a different way.
Lets say we have a large gallery with
old paintings. Thousands of paintings.
The gallery has just one display room,
though, and it has walls for just ten
paintings. The gallery manager has to
switch paintings now and then when the
visitors get bored and want to see
some new paintings.
Every displayed painting needs a
frame. Without a frame, it can't be
put on a wall. Frames are expensive to
make, and take up a lot of space. The
frame maker guy want have time nor
money to build the thousands of frames
needed.
He finds out that he want be needing
frames for all the paintings that is
not shown at the moment. He would only
need ten frames for the currently
displayed paintings. When the gallery
manager takes down a painting, the
frame maker stores the frame, and when
the gallery manager put up a new
painting and asks the frame maker for
a frame for it, the frame maker
returns the frame from the previous
painting again.
One day, the
needed-space-between-paintings-regulations
gets changed for no good reason. The
gallery manager is able to put up two
more pictures in the display room. He
picks two paintings from the store
room, and asks the frame maker for
frames. The frame maker has no spare
frames, and need to make two new
frames.
Now, lets say that the gallery is a TableView, and all the paintings are rows of data. The display room with space for ten visible paintings, is the screen, with space for ten visible rows. Each visible row would need a cell, just like each displayed painting would need a frame.
In the end, you shouldn't care that much about saving resources by reusing one cell. That's TableViews responsibility. It's an implementation detail of the TableView how many cells is needed and how it is used. The protocol defines how you can ask the TableView for an reusable cell, and the documentations states that you should. That should be enough. Demo projects shows that TableView can manage very large amounts of data. If your projects struggles with performance because of instantiating 10-20 cells from nib, you probably got some problems with your nib file or something. There are some discussions, though, about the performance of loading from nib versus building cells in code. It may be interesting to you.
I had some very weird behavior that sounds very much like what you are describing some time ago.
Eventually I found that the problem was that I had just added a table view cell to a xib which contains other items such as the parent table view and controller. What I had to do was create a seperate xib for each table view cell individually. I think the issue was that loading the table view cell from an incorrectly built xib was confusing the issue.
As Vegar said there are a lot of tutorials on how to do it.
Related
Short Explanation
Currently I have a UITableView which contains cells of videos from a website. In addition each cell (which represents a video) has a specific thumbnail image. These images are downloaded asynchrounously using NSURLConnection (so I do not have to worry about threading myself). When these image objects have downloaded they simply notify the UITableView to refresh the cell it belongs to.
My Problem
As standard there will be fetched 10 new videos to the UITableView each call. This means that the user is allowed to push a cell at the bottom of the UITableView to request 10 new videos and so forth. The problem here is that there will quickly be a whole lot of memory usage because data for all thumbnail images (in the UITableView) will still exists no matter what.
Is there a smart way in which you can de-allocate image objects that aren't currently in the view?
Also, the UITableView simply renders all its cells directly from an array I have of all video objects ever fetched from the website. This means there is no limits as to how many times the user can request new videos, making this array bigger and bigger all the time. Is it correct to keep these kind of arrays in memory all the time? Or should you instead delete the ones which goes out of the view and request them again later on?
Thank you in advance
If you're using the standard method approach of queuing and de-queuing cells in your tableview, then you really should only be using a handful of cell views (and their related thumbnails) - eg, the total number of cell views allocated should never be more than the maximum number viewable on screen, and by extension, the number of allocated/references to thumbnail images should not exceed the total number of cells. Each time you present a row representing a new movie, the imageview is as likely reused as it is new.
What you want to consider is that your data array (an array of your downloaded clips, with potentially unlimited objects therein) is completely discrete from your tableview's array of cells, which is going to be a relatively small number.
My advice would be to conceive a download cache of images. Basically, as your thumbnails are downloaded, assign each a unique string id, and write the actual image file out to disk. Assign the unique ID string to your model object. When your tableview prepares to present an image, locate the image for the cell using the id string, load the image from disk, and populate the image view. As long as you're not retaining the image, the image should be released when the cell presenting it goes off-screen. You can additionally empty the cache of loaded images under low memory conditions.
If you create your own class VideoTableViewCell as a subclass of UITableViewCell and you give your cell a property for an image this image will be released as soon as the cell goes off the screen. The cell and the image will be recreated as soon as the Cell come back on the view.
For the videos I would store all downloaded video files in the documents folder and store there url in an array. If a cell comes visible or is clicked simply load the file from the hard drive.
In my iPad application, I've made a segmented control and one segment of which -when clicked- displays a long list (about 300) with images and labels from the local SQLite database. This is taking a lot of time to load and puts the app activity to halt while it's loading all of it from the database.
Although I've applied an activity indicator for the time being, but that looks very shoddy. Can anyone tell me how to apply Lazy Loading in a way that When the button is clicked to open that view, instead of loading all the content at once, it fetches only the content that's displayed on the content initially (about 9 images with lablels).
Thanks in advance.
You should implement paging on the list. Load first 25 item and then add button ("Next 25") on tableFooterView, which will load another 25.
If you use a UITableView, you might have a better chance.
A UITableViewCell loads cells one at a time(gives you the index of the item it is trying to load), so if you use a table view, it will only load the number of items that it needs to display. You tell it how many items there are, how tall they are, etc.
It also reuses cells, so it gives a lot better performance than creating 300 different views in memory at a time.
A UIScrollView doesn't know about your "items", so it lets you push as many items as you want into a view, and then adds a scroll bar. No optimization here for memory usage, or database access.
This may turn out to be 2 questions in one, but I believe solving the first will go most of the way to solving the second.
First, I have created a view in Interface Builder (I know!) and I load the xib file into my view controller in the app delegate. All of this runs smoothly and as expected.
The view consists of 3 table views, two of a similar size and one small one in a corner.
A cell is added to one table view a pan gesture is added to allow movement around the scene.
However, if a cell is moved from it's table view, it appears to go BEHIND the parent view, as per the screenshot below:
The grey line between the two tables is the gray background of the parent view. If the user still has his/her mitts on the cell, they can drag it into view but how can I make it so that all table views are on the same 'layer'?
I.e. so that dragging a cell from one table view to another with show the cell hovering over both views.
This leads me onto my second question, which I will not ask yet as I believe the solution to this will solve my current issue. but I will explain for further clarity.
In my pan gesture, I use a point inside check to see if the cell is within a table view, currently panning a cell from any table makes it print. It is almost like the views take up the whole screen, even though they are sized not to?
All ideas welcome! Thanks!
This was solved by listening for the cell leaving the bounds of the table view, when this happened, I transferred the cell across the table view that it has been dragged to, adding it to the bottom of the list.
Not the most elegant solution and you do lose the touch but it does work,
Suppose I have a table view, and I want to implement something like this:
The table view contains n cells by default.
New cells will be added at the top(head) of the table view.
The data source of the table view is a mutable array.
The problem is when I use [tableview reloadData], the new data is always shown at the top of the tableview, but what I want is remain the old visible cells at the old position, means no refresh after reload data. I had tried some solutions, and I found out that if I added new cells at the tail, and update the tableview, the old visible cells will remain old position without any extra effort. But I don't know how to remain the old visible cells at the old position if I add the new cells at the top.
As a reference, I think the official Twitter app for iPhone just implemented what I want in the time line view, but I don't know how to archive it.
Guys, have any idea?
Thanks a lot.
-Tonny Xu
[Update] It's a little bit hard to describe what I want in text. I added some pictures.
As the picture shows[the link is below], the default cells is started from section California, I want to added 3 new cells before "Brea", what I want is after I added "New cell 1,2,3", the cell "Brea" is still remain the position where it was. In another word, the new cells 1,2,3 are not visible after updated.
Sorry, because I don't have enough reputation to use image, please visit this url http://i.stack.imgur.com/S9jJl.png
I had figured out how to implement this, hope this can help somebody else who wants the same effect.
The point is that UITableView is a subclass of UIScrollView. Thus, UITableView has all the properties of UIScrollView, especially one will work for this: UITableView.contentOffset, this can also be animated using [UITableView setContentOffset:animated:].
To archive the same effect as Twitter for iPhone official app, we need to know every time how much offset is added or deleted. Remember the former offset and set the offset +/- delta offset without animation.
Done.
Just answered a similar question with code snippets here
Keep uitableview static when inserting rows at the top
Basically:
Save the scroll offset where you would have called beginUpdate:.
In place of calls to InsertCell you add the cell's height to the saved offset.
Where you would have called endUpdate:, call reloadData, then scroll by the saved offset with NO animation.
I am new to iPhone development. I am parsing a xml and display the title, date, contents and image in the cell. Now the scrolling is not smooth, it is stuck.
How can I increase it? I have already applied lazy loading in another view, I am not able to apply in the new view. So how can I increase the scrolling performance? Can I check for any condition that if image is already loaded in the image view, so I can stop once again the loading of image view?
What I do to guarantee fast scrolling in a table is to subclass my own UITableViewCell where I implement all properties that I need.
Whenever that tablecell is initialized I draw the properties of the cell on the cell itself. So when you need an image on there, dont use an UIImageView, but just draw the image on the cell. Also all the text I need I just draw on there.
After a long stuggle finding out how to do all this, I also found a nice blog post about this.
http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
First of all, check if you're not resizing the images - that takes a lot of computing power, and will slow down the table for sure.
Second of all, check for view hierarchy - complicated view hierarchy means bad performance. Rembemer to use as much opaque views as possible, and when you're using non-opaque views don't make the cells too complex(by complex Apple means three or more custom views). If the views are too complex - use drawRect method for custom drawing of the content.
There's a great example on how to achieve this provided by Apple called AdvancedTableViewCell (here's a link), and there's a great tutorial by Apple about table view cells (another link).