I would like to have a UITableView with a UIWebView in each cell. The UIWebView should have the contentSize so that it shows its whole content without scrolling. My problem is the HeightForRowAtIndexPath because the content has to be loaded first before it can return a value.
I did something like this in this old and maybe no more up-to-date code:
https://github.com/Daij-Djan/DDUtils/tree/master/ui/M42WebviewTableViewCell%20%5Bios%5D
Disclaimer: My own open source code
How it works:
In the dataSource:
you return the cell's preferredHeight (which is 0 if there is no content)
you also implement the delegate - (void)tableCellDidLoadContent:(M42WebviewTableViewCell*)cell;
you reload your table which will invalidate all Heights. When you now return preferredHeight it is valid and the views appear alright.
I would subclass UITableViewCell in this (very MVC) way:
#interface CustomTableViewCell : UITableViewCell
#property (nonatomic,strong) id model;
+(CGFloat)heightForModel:(id)model;
#end
Model can be whatever your want. This is a very generic example.
In heightForModel you calculate the exact height of the cell.
In the init method of CustomTableViewCell, you should add a blank UIWebView to the cell.
When you "set" model property, you should refresh cell UI.
Then in the class that implements the delegate methods (assuming that cell models are stored in a NSArray called models):
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [CustomTableViewCell heightForModel:self.models[indexPath.row]];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomTableViewCell class])];
cell.model = self.models[indexPath.row];
return cell;
}
Remember to register your custom cell!
Related
I am creating a tableView which has collectionView inside.
what I did :
1. Created a table view inside a controller.
2. created a custom tableViewCell with collectionView inside of that and providing it with tag
3. Created a custom collectionViewcell.
Now i made controller the delegate and datasource for both collectionView and tableView.
Now tableView should have multiple tableViewcells and each tableViewCell has CollectionView
One tableViewCell has one section.
Problem :-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
self.arrayModel = self.SlotsArray[indexPath.section];
}
But here is a catch : lets say my screen size shows two tableViewcell, then all methods of tableCells are called first and then all methods of collectionView are called instead i want that after 1st section of tableViewcell is created , it should call collectionView methods and create it. then go to tableView method make 2nd section and then create its collectionView.
Is there away to do this.
Somewhere i read on net thet i will have to subclass CollectionView and add a property index and then while setting viewcontroller as delegate and datasource , set index also.
But i have created CollectionView By nib and adding it to the tableCell class by "TAG"
self.imageCollectionView = [self.contentView viewWithTag:2511];
Can you tell how can i achieve this?
In your case, you should have one table view, having cells. Each cell will have a collection view inside it.
Number of table view cells will equal to the count of your data-source. As we all know, table view cell will be created one by one and assign each cell a data source which will be used to fill your collection view (which is inside each cell).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifierCategory forIndexPath:indexPath];
NSArray *arrVideos = [parentArray objectAtIndex:indexPath.row];
[cell setCollectionData:arrVideos];
return cell;
}
Now, inside that table view cell class, implement setCollectionData method to fill its collection view:
- (void)setCollectionData:(NSArray *)collectionData
{
self.arrCollectionData = collectionData;
[self.yourCollectioView reloadData];
}
Now, each collection cell would be feed from this array:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
VideoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifierVideo forIndexPath:indexPath];
NSDictionary *_video = [self.arrCollectionData objectAtIndex:[indexPath row]];
// Use this dictionary
[cell.lblVideoTitle setText:_video.title];
––––––––––
––––––––––
return cell;
}
Refer following image:
Hope it helps you !!!
You can take a look on this, surely it will help you to implement horizontal scrolling collection view inside uitableviewcell.
https://github.com/ThornTechPublic/HorizontalScrollingCollectionView/blob/master/GithubImages/videoGrid.gif
https://github.com/agusso/ASOXScrollTableViewCell
I want a tableview inside another tableviewCell like the following image.It shows one complete cell with a few details and a tableview. How can i do this?I was following this link Link.This is an old code .It is using xibs.I dont have any idea where to set the delegate for the inner tableview.Please help.Any suggestion will be realy helpfull.
My first idea would be:
Subclass UITableViewCell ("MainTableViewCell") and extend it with UITableViewDelegate and UITableViewDatasource.
Next to all the properties you need in "MainTableViewCell" add a TableView "tableViewFilms" and an array "films" for the Films. Also don't forget to add the datasource methods for a tableview to the implementation file.
To easily setup a cell I add a setup-method to the header-file. Which can be called once the cell is instantiated. You can modify it as you want, give it as many parameters as you want and in the implementation (see step 4) set datasource and delegate of your inner tableview.
- (void)setupCellWithDictionary:(NSDictionary *)dict AndArray:(NSArray *)filmsForInnerTable;
You can call this method in your datasource method, once a cell is instantiated:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MainTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainTableViewCell" forIndexPath:indexPath];
NSDictionary *dict = (NSDictionary *) allDataDictionaries[indexPath.row];
[cell setupCellWithDictionary:dict AndArray:filmsForInnerTable];
return cell;
}
Subclass UITableViewCell another time: "FilmTableViewCell"
When setting up the a Cell of "MainTableViewCell", set the delegate and the datasource of "tableViewFilms" to self (object of "MainTableViewCell").
- (void)setupCellWithDictionary:(NSDictionary *)dict AndArray:(NSArray *)filmsForInnerTable{
self.films = filmsForInnerTable;
self.tableViewFilms.dataSource = self;
self.tableViewFilms.delegate = self;
[self.tableView reload];
//more instructions
}
Populate the tableview with the data from the array "films" using "FilmTableViewCells".
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FilmTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FilmTableViewCell" forIndexPath:indexPath];
Film *film = (Film*)films[indexPath.row];
[cell setupCellWithFilm:film];
return cell;
}
Hope this helps.
Don't forget to use Outlets, the method definitions and to set the reuse-identifiers for the cells!
Check my answer in this ios 8 Swift - TableView with embedded CollectionView. You have replace that UICollectionView with UITableView.
Everything else is pretty much the same. Its just a head start with UITableView and UICollectionView created programmatically.
I can change it accordingly if you don't understand.
I am trying to work with UICollectionView that is a part of the UITableViewCell.
So the issue I faced, the collection view for visible table cells works ok, but when I start scroll table and table starts create new cells that where invisible then I faces with some behaviour that duplicate first table cell. For example I have scrolled collection view in first table view cell, then I scrolled table view down, and what I see the new cell have the same state as my first cell had. You can check source here repo and the video here to understand what I am talking about.
Have a property of UICollectionView in CustomTableViewCell.h and bind it in Main.storyboard. Let's assume declared it like this:
#property (strong, nonatomic) UICollectionView *collectionView;
In ViewController.m change cellForRowAtIndexPath method, so it reloads inside UICollectionView every time it need
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomTableViewCell"];
[cell.collectionView reloadData];
[cell.collectionView scrollRectToVisible:CGRectZero animated:NO];
return cell;
}
Of course, you need to import CustomTableViewCell.h on ViewController.m file.
-[UITableViewCell prepareForReuse]
Problem with reuse identifier in UITableView cell, if you use single cell identifier in cellForRowAtIndexPath this problem will occurs
1. Use Dynamic cell identifier for each row, like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:#"CustomTableViewCell_%d",indexPath.row]];
}
2.Store content offset for each collection view in array, and reset collection view content offset
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
......
[cell.collectionView setContentOffset:scrollingPoint animated:NO];
return cell;
}
(note: The tableView I am using is from the Parse.com iOS SDK - PFQueryTableViewController)
Scenario = I have a TableViewController that has two different types of cells (each with their own identifier). Each object upon being queried and loaded into the datasource is checked if a key on the object is true. Depending on the result I dequeueReusableCellWithIdentifier to the correct cell.
-(PFTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
myTableViewCell *cell;
if ([object[#"orientation"] isEqualToString:#"left"] || [object[#"orientation"] isEqualToString:#"right"]) {
myTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
else {
myTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell2"];
}
This all does its job. Each cell is being loaded at the correct indexPath.row and everything. Problem is my tableView "Row Height" itself does not readjust for the new cell. This causes overlapping of cells and makes everything ugly. I can tell the tableView in storyboard to set the row height to whatever the larger of the two cell heights is, but that leaves big spaces in-between cells too which also makes it look ugly.
Question = It is my belief (and correct me if I'm wrong) that I need to use the
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
method in order to achieve this. Where I need help is I am not sure how to set the height of each cell at indexPath depending upon the 'identifier' that I gave each cell in the cellForRowAtIndexPath method.
What I'm looking for = Something like this (please excuse the SuedoCode)
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([cell.identifier isEqual:#"Cell"] {
return 100;
}
else {
return 200;
}
}
ANSWER: I figured it out! (I marked the answer below as accepted because it pointed me in the right direction)
Because I am using a PFQueryTableViewController all I had to do this...
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
if ([object[#"orientation"] isEqual:#"left"] || [object[#"orientation"] isEqual:#"right"]) {
return 100;
}
else {
return 200;
}
}
First, some things to keep in mind. heightForRowAtindexPath is calledbefore CellForRowatIndexPath, and simply says, if object is at indexPath X, then return Y or Z.
The more correct approach might be to subclass the tableCell class, set a property in the .h file and then figure out the path... I'll give you a dirty way :)
Create an NSMutableArray property (don't forget to init it somewhere/somehow), and based on your dataSource, populate it with Height A or Height B (a float). Now, back in heightForRowAtIndexPath, you can say something to the effect of:
return (int)self.myMutableArray[indexPath.row];
I am using a static cell layout in a UITableView. During the workflow I need to address the attributes of a specific cell. All cells have an identifier but i did not found a way to actually address the cell using the identifier.
Thanks,
em
The reason why cellForRowAtIndexPath: is not reliable is probably this (from the documentation):
An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
If you ask for a cell that is not visible on screen, it may have been purged from the table view.
Since you mention that you use a static cell layout in your tableview (I assume you don't rely on cell reuse), you could consider keeping the cells as private properties:
In the private interface:
#interface MyViewController ()
#property (nonatomic, readonly) MyTableViewCellClass *myStaticCell;
#end
In the implementation:
#implementation MyViewController {
MyTableViewCellClass* _myStaticCell;
}
- (MyTableViewCellClass*) myStaticCell {
if (!_myStaticCell) {
// Initialize _myStaticCell
}
return _myStaticCell
}
You can then call this lazy loaded property when tableView:cellForItemAtIndexPath:is called and whenever you need to modify it.
Note that this approach is only recommended if you have a tableview with static content and don't rely on cell reuse.
Here indexPathSelected is the selected specific cell
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPathSelected inSection:0]];
indexPathSelected = indexPath.row;
}