I have been using objective c for a few months now, using different foundation classes and generally playing around with the language.
In my own experience nothing has been more confusing than UITableView; Below is a bit of code that does not do much.
//the header file
#interface SLDataBankListTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
//implementation
#interface SLDataBankListTableViewController ()
#property (strong, nonatomic) SLDataSourceObject* dataSource;
#end
#implementation SLDataBankListTableViewController
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
_dataSource = [[SLDataSourceObject alloc] init];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _dataSource.dataBankNames.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"reuse"];
cell.textLabel.text = [_dataSource.dataBankNames objectAtIndex:indexPath.row];
return cell;
}
#end
I have successfully used this class for over a dozen times and every time I make some stupid mistake like now. Finally I gathered some courage and decided to ask for help.
What are the subtle things, nuances of this class that I don't seem to grasp?
Edit: this is a part of a small program, other parts of it work fine, but the table displays nothing; Some changes have been recommended that are important but did not solve the problem.
It is a little hard to debug without known what is not working, but I see some things which might help you out.
UITableViewController has its own tableview but you seem to have another tableview wired up in a nib. Either use the UITableViewController tableview, or create your own, don't do both.
in cellForRowAtIndexPath you are creating a new cell every time instead of reusing the cells you have.
The delegate methods for the tableview can be called before viewWillAppear. You should create your datasource object earlier. I suggest viewDidLoad:. (Another reason viewWillAppear is a bad choice is that it can be call multiple times, and you can end up creating and destroying many datasource objects for no reason)
Hope that helps.
The big thing to remember about a table view is that it's a way for user to interact with an array of objects. The array is represented by a datasource, and the datasource methods describe what the view needs to know:
how many objects are in the array (called numberOfRowsInSection:)
how to display each one of the objects (called cellForRowAtIndexPath:)
To answer the latter question, the datasource must answer a view. That view's job -- like any view -- is to represent an object for the user. In row the table view uses a UITableViewCell.
The datasource array can be arbitrarily large, so directly mapping UITableViewCells to its elements can get arbitrarily expensive in memory terms. Rather than create a cell for every object in the array, the table view reuses cells.
When a cell scrolls off the top, the "new" one that appears at the bottom isn't new, it's the old cell handed back to the datasource to be reconfigured for the new row. To accomplish this, your datasource is expected to not allocate a new cell, as #JonRose correctly points out, but to ask the table view for a reused cell using dequeueReusableCellWithIdentifier.
Related
I am trying to embed a UITableView in my View Controller (not a TableViewController).
I am unable to get any of my data to show.
Here is the relevant code:
M file:
#interface ViewController () <CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) NSMutableArray *Device_array_name;
#end
.
.
.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text=self.Device_array_name[indexPath.row];
return cell;
}
I've also defined number of sections and rows, and my array does indeed have content. I'm pretty new to iOS development so I've no doubt missed something.
Thanks
Delegates & Datasources should be defined for UI Controls to get called their respective delegates.
For Instance : In your viewDidLoad function define
tableView.delegate = self
tableView.datasource = self
It will work.
Set the array allocation in your view did load.
Device_array_name=[NSMutableArray alloc]init];
Set the tableview delegates in viewdidLoad
tableview.delegate=self;
tableview.datasource=self;
After give the count of an array in Number of rows in section
return Device_array_name.count;
In your cellForRow AtIndexpath method
cell.textLabel.text=[self.Device_array_name objectAtIndex:indexPath.row];
There could be 4 reason (most probably)
1> You delegate and datasource are not set
tableView.delegate = self
tableView.datasource = self
2> Your datasource array is nil (Device_array_name)
3> Your IBOutlet is not connected with tableview
4> Your tableview frame is becoming zero due to autloayout or something else (check console at runtime)
I think you just need to initialize the delegate and dataSource for your tableView property to your ViewController.
If you embed this way, you don't have default setting for the UITableView like you have when you use UITableViewController directly. You will need to set it up by yourself. Most likely, it's because you have not wired up your delegation object. Second will be, your UITableView is invisible. That also causes the problem because the delegate methods will not be called.
i think go to storyborad and set delegate,datasource in there.if you using by storyborad.i hope will work.
Here is my list of the possible problems:
The #IBOutlet for the table view is not connected in Interface Builder. Solution: check connection in Interface Builder.
The outlet is set, but the view controller placed in the storyboard is not of your custom UIViewController subclass, but the default UIViewController. In this case, none of your code will be executed. Solution: specify the view controller's class is the identity inspector (center tab).
The dataSource property of the table view is not set to your view controller. Solution: Assign it in either Interface Builder (storyboard) or programmatically inside viewDidLoad() or later.
You are not implementing the UITableviewDataSource protocol method:
tableView(_:numberOfRowsInSection:) (i.e., the table view "thinks" it has 0 rows to display). In this case, tableView(_:cellForRowAtIndexPath:) will not be called (you can confirm this by placing a break point). Solution: Implement the method and return the count property of your model object array.
The array that serves as the data source for your table view is empty (has zero elements). Solution: Make sure the array contains your model objects.
I might have left some other common mistake out... Let me know.
Addendum: Unless you have a compelling reason not to, I would strongly suggest that you use a stock UITableViewController instead of manually adding a UITableView to a plain UIViewController. You get a lot of functionality and setup for free.
I have 2 custom UITableViewCells in my TableView wich are using a UICollectionView. How can I separate each Data Model used for each TableViewCell in a different section like this:
if (indexPath.section == 0) {
//Each CollectionViewCell should display Profile Picture of User
} else if (indexPath.section == 1) {
//Each CollectionViewCell should display Pictures uploaded in this group
}
I am using the TableViewCell provided by Ash Furrow: http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell
I want to achieve the same results as this guy: multiple UICollectionviews inside a UItabelview with sections - swift
but I have quite a different code and his is written in swift, and I don't don't know to solve my problem by using the solution provided answer given by #pbasdf
Edit: These are the cells, I want the "Going" Cell to show Profile Pictures of the Users and the "Photos" Cell to show images uploaded to this group, but both cells are currently using the same data model and I don't know how to use different data models for each cell (the cells are divided by sections).
Any implementation of a table view or a collection view should have a dedicated model.
Simply pass the model down.
NSArray > NSArray > Profile Pictures
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
NSArray *list = self.lists[indexPath.row];
[cell configureWithList:list];
return cell;
}
#interface CustomTableViewCell () <UICollectionViewDataSource>
#property (nonatomic, weak) NSArray *list;
#property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
#end
#implementation CustomTableViewCell
-(void)setList:(NSArray *)list {
_list = list;
[self.collectionView reloadData];
}
-(void)configureWithList:(NSArray *)list {
self.list = list;
}
EDIT: After reading this, I decided to build this control -> cocoacontrols
Sounds a bit tricky, but I think it should be possible without too much work.
You would need to tell the table view how many sections you have, and change numberOfItemsInSection: to return the number of rows in each respective section (looks like only one, based on your screenshot). Finally, we need to augment the AFIndexedCollectionView collection view subclass. Right now it stores an integer index, but we would need to modify it to store an index path instead. Then the collection view's cellForItemAtIndexPath: method would use that index path to determine which section the collection view is in.
It's a bit tricky, might make sense to draw the UI out on paper and reason about it that way. Good luck!
I won't go into the WHY on this one, I'll just explain what I need.
I have a property in my implementatioon file like so...
#property (nonatomic, strong) MyCustomCell *customCell;
I need to initialize this on viewDidLoad. I return it as a row in cellForRowAtIndexPath always for the same indexPath, by returning "self.customCell".
However it doesn't work, I don't appear to be allocating and initializing the custom cell correctly in viewDidLoad. How can I do this? If you know the answer, save yourself time and don't read any further.
Ok, I'll put the why here in case people are curious.
I had a table view which was 2 sections. The first section would always show 1 row (Call it A). The second section always shows 3 rows (Call them X, Y, Z).
If you tap on row A (which is S0R0[Section 0 Row]), a new cell would appear in S0R1. A cell with a UIPickerView. The user could pick data from the UIPickerView and this would update the chosen value in cell A.
Cell X, Y & Z all worked the same way. Each could be tapped, and they would open up another cell underneath with their own respective UIPickerView.
The way I WAS doing this, was all these cell's were created in the Storyboard in the TableView, and then dragged out of the View. IBOutlets were created for all. Then in cellForRAIP, I would just returned self.myCustomCellA, or self.myCustomCellY.
Now, this worked great, but I need something more custom than this. I need to use a custom cell, and not just a UITableViewCell. So instead of using IBOutlets for all the cells (8 cells, 4 (A,X,Y,Z) and their 4 hidden/toggle-able UIPickerView Cell's), I created #properties for them in my implementation file. But it's not working like it did before with the standard UITableViewCell.
I'm not sure I'm initializing them correctly? How can I properly initialize them in/off viewDidLoad so I can use them?
.m
#interface MyViewController ()
#property (strong, nonatomic) MyCustomCell *myCustomCellA;
...
viewDidLoad
...
self.myCustomCellA = [[MyCustomCell alloc] init];
...
cellForRowAtIndexPath
...
return self.myCustomCellA;
...
If only I understood your question correctly, you have 3 options:
I would try really hard to implement table view data source with regular dynamic cells lifecycle in code and not statically – this approach usually pays off when you inevitably want to modify your business logic.
If you are certain static table view is enough, you can mix this method with overriding data source / delegate methods in your subclass of table view controller to add minor customisation (e.g. hiding certain cell when needed)
Alternatively, you can create cells using designated initialiser initWithStyle:reuseIdentifier: to instantiate them outside of table view life cycle and implement completely custom logic. There is nothing particular that you should do in viewDidLoad, that you wouldn't do elsewhere.
If you have a particular problem with your code, please post a snippet so community can help you
I suggest you to declare all your cells in storyboard (with date picker at right position) as static table and then override tableView:heightForRowAtIndexPath:
Define BOOL for determine picker visibility and its position in table
#define DATE_PICKER_INDEXPATH [NSIndexPath indexPathForRow:1 inSection:0]
#interface YourViewController ()
#property (assign, nonatomic) BOOL isPickerVisible;
#end
Then setup initial value
- (void)viewDidLoad {
[super viewDidLoad];
self.isPickerVisible = YES;
}
Override tableView delegate method
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath isEqual:DATE_PICKER_INDEXPATH] && !self.isPickerVisible) {
return 0;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
And finally create method for toggling picker
- (void)togglePicker:(id)sender {
self.isPickerVisible = !self.isPickerVisible;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
which you can call in tableView:didSelectRowAtIndexPath:
According to your problem, you can create pairs (NSDictionary) of index path and bool if its visible and show/hide them according to that.
Here's what I was looking for:
MyCustomCell *cell = (MyCustomCell *)[[[UINib nibWithNibName:#"MyNibName" bundle:nil] instantiateWithOwner:self options:nil] firstObject];
First of all I am very new in Objective C and iOS development.
Currently I am trying to use TableView
I have property
#property (strong, nonatomic) IBOutlet UITableView *tableView;//for table view
Now I seen something like this,
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [data count];
}
What is exact meaning of the code. What I am doing with tableView here, and how name of property and name of message is same, i.e. tableView.
In first line you are declaring property of an tableview which you create by storyboard.
The goal of the #property directive is to make it easy to create and configure properties by automatically generating these accessor methods. It allows you to specify the behavior of a public property on a semantic level, and it takes care of the implementation details for you.
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [data count];
}
This is a tableview delegate method which define the no of row in a particular section in table view
data is your array name contain element which you want to show in tableview cell
[data count] return the number of row in your table view
for more detail check
property
table view
What I am doing with tableView here
tableView:numberOfRowsInSection: is a method from the UITableViewDatasource Protocole to do simple, the object of type UITableView will do something like it :
[self.delegate tableView:self numberOfRowsInSection:section]
and so, if you do tableView.datasource = self in your viewController, it's this object which will receive the message and so, need to answer to the tableview.
and how name of property and name of message is same, i.e. tableView.
#property is just an indication for the compiler to create accessors and iVar in your class, that why it seems to do many things at the same time.
I have one parent class with one tableview.
That class is the delegate and datasource of that tableview as well.
Now I subclassed (derived) that class and made a child class.
I have one tableview in child class too.
Then I defined delegate and datasource functions in that child class, but it overrides parent class tableview data source/delegate methods.
But I want both of them to be separate.
However my Requirement is as Follows :
I want to retain a search bar and side button, on the top of all the viewControllers that search bar includes , a recent searches terms table underneath that.
So i thought of defining parent class for that and subclass other viewControllers from that class.
Am i doing it the right way ?
I assume you are talking about a view controller class. If I understood you right, then you are about to mess it up. Delegation is a way to avoid subclassing. Of course you can subclass the delegate - no problem. But you want a table view in the super class that owns a table in its view. And you want a subclass that has another table in its view plus the table that the superclass owns.
That is not impossible. But from your subclass' point of view, your subclass owns two table views. Even that is possible. Your view controller is the delegate of two tables (regardless of where in the view hierarchy they are declared and instanciated). When you now override the delegate and data source methods theny your subclass must either:
Determine which table it is dealing with/being called from. And then serve both tables appropriately.
Determine wich table it is dealing with/being called from. And then serve "its own" table appropriately and calls [super sameMehtod:withSamePamaters] to ensure that the superclas can still provide the data and server as delegate.
Which of both is smarter depends on the context and what you are about to achieve in detail.
A way of determinnig which table's delegate was called can be done by tagging the table views (do not use 0 as tag) or by comparing the tableView parameter of the delegate method with the corresponding properties (IBOutlets in this case). (In other cases you can compare the sender parameter with the IBOutlets. But tagging is probably easier to understand when reading the code later.)
Let's look at an example of the UITableViewDataSourceDelegat:
Your superclass implements:
#interface MySuperTableViewController:UITableViewController <UITableViewDelegate>
// There will be something in here.
// But it inherits self.tableView from UITableViewController anyway. We leave it with that.
#end
#implementation MySuperTableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// This method creates or re-uses a cell object and sets its properties accordingly.
}
#end
And your subclass:
#interface MySubTableViewController : MySuperTableViewController // no need to declare the delegate here, it is inherited anyway
#property (weak, nonatomic) IBOutlet UITableView *mySecondTableView; // self.table will be used by the superclass already.
#end
#implementation MySubTableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.table) { // This call refers to the one talbe that is managed by super
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
// This method now creates or re-uses a cell object and sets its properties accordingly.
// You may want to check wether tableView == self.mySecondTableView etc.
}
#end
(This comes from scratch, not syntax checked etc. Do not expect this to run properly right away :)
BUT ... please re-consider your class structure. I am afraid you are getting lost in some rather unlogical class hierarchy. There is nothing wrong with having two talbes managed by a common view controller even without this subclassing-thing. And there is nothing wrong with using multiple tables in a view where each of the tables has its own delegate (can be a view controller). Since iOS 5 (or was it introduces with 6) we can use the UIContainerView for that purpose and nicely build it up in IB/storyboard.
try this,
ViewController.h
IBOutlet UITableView *firstTable;
IBOutlet UITableView *secondTable;
ViewController.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
if (tableView == firstTable) {
return 1;
}
else if(tableView == secondTable)
{
return 1;
}
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
if (tableView == firstTable) {
return [arrItems count];
} else if(tableView == secondTable)
{
return [arrData count];
}
return 0;
}
etc etc ....