I have an image property on my table view cell. I want to populate this image to an image.
My cell looks like:
//.h
#interface GAFriendStatusTableViewCell : PFTableViewCell
#property (weak, nonatomic) IBOutlet UIImageView *friendImage;
#end
//.m
#import "GAFriendStatusTableViewCell.h"
#implementation GAFriendStatusTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.friendImage.image = [UIImage imageNamed:#"user.png"];
}
return self;
}
- (void)awakeFromNib
{
// Initialization code
}
#end
This doesn't set the image. How can I set the image for this cell within the cell class?
Since your image view is an IBOutlet, I'm assuming you've got your cell layout defined in a .xib or storyboard. That being the case, your initialization code should go in the awakeFromNib method. This is the method that is called when the cell is created from the nib. That's why the //Initialization code comment is there. initWithStyle:reuseIdentifier: is never called in this scenario, which is why your image is not appearing.
Set it in - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Otherwise if you have a Nib or Storyboard you're using then set the default in the prototype cell you are using.
Related
So I have a custom UITableViewCell:
TestTableViewCell.h
#import <UIKit/UIKit.h>
#interface TestTableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *testCellLabel;
#end
TestTabelViewCell.m
#import "TestTableViewCell.h"
#implementation TestTableViewCell
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
_testCellLabel = [[UILabel alloc] init];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
And then I have view controller with a table view that uses the custom table view cell. However this issue is that I don't want to use dequeueReusableCellWithIdentifier within the cellForRowAtIndexPath. I instead want to have an array of cells.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#end
ViewController.m
#import "ViewController.h"
#import "TestTableViewCell.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) NSArray *myTableViewCells;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSArray *)myTableViewCells {
TestTableViewCell *cell1 = [[TestTableViewCell alloc] init];
cell1.testCellLabel.text = #"one";
cell1.backgroundColor = [UIColor blueColor];
TestTableViewCell *cell2 = [[TestTableViewCell alloc] init];
cell2.testCellLabel.text = #"two";
cell1.backgroundColor = [UIColor greenColor];
if (!_myTableViewCells) {
_myTableViewCells = #[cell1, cell2];
}
return _myTableViewCells;
}
#pragma mark - UITableView delegate functions
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.myTableViewCells.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TestTableViewCell *cell = self.myTableViewCells[indexPath.row];
return cell;
}
#end
The problem is that there is no testCellLabel appearing in the table view cell. I know the cells are there, because I set their background colour.
After talking to a few people, apparently I need to do some sort of loading from the XIB or the NIB for the UI to load properly? Even though the label is defined in the cell in the storyboard.
I know this is going against the norm and that Apple really wants you to use dequeueReusableCellWithIdentifier, but I know it won't work in the situation I need it in. I have done the reading on that much so please don't just tell me to use it. This code example is just very basic for example sake and ease of use.
Any help would be greatly appreciated.
TestTableViewCell *cell1 = [[TestTableViewCell alloc] init];
Creates a new TestTableViewCell object and does not instantiate it from the storyboard like you're thinking it does. Therefor all outlets created will be nil and simply not show up. The fact that you can set the background colour is not evidence that your implementation works.
You need to use dequeueReusableCellWithIdentifier. You say that it doesn't work for your problem.. show me how it doesn't work and I will tell you why you're wrong.
Edit
I see in your comments you say your cell needs a custom setter. Well, when you use dequeueReusableCellWithIdentifier you can do all setup work in awakeFromNib (If using a xib file) OR initWithCoder if you are using the storyboard.
You can create cell without dequeueResableCellWithIdentifer.
[[UITableViewCell alloc]initWithStyle:<#UITableCellStyle#> resueIdentifier:<#(nullable *NSString)#>]
I have 2 UIViewControllers, the 2 ones are containing EXACTLY the SAME UITableView(with its custom cells and delegate methods).
My question is their any way to "centralize" the UITableView UI and code(datasource and delegates), so that I just have to modify in one file instead of 2 .
following up on my comment, the table view in the xib in your father vc and the delegate methods in your father vc are just in the same place because you chose it to be like that, the table view and the delegate methods are actually quite detached.
so create a new object, say FatherTableController which implements UITableViewDatasource and UITabelViewDelegate and copy those methods out of your FatherViewController into this FatherTableController
now in your FatherViewController, go like
FatherTableController tableController = [FatherTableController new]; //should be a property or a singleton
self.tableview.delegate = tableController;
self.tableview.datasource = tableController;
now you can do that in both your separate vc's that use the same table, and even use the exact same table contoller between the two views if you share it in some way (possibly via a singleton pattern, which can be useful for sharing state between the two view controllers)
Solution:
#interface FatherViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *parentTableView;
#implementation FatherViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.parentTableView.delegate=self;
self.parentTableView.dataSource=self;
}
//declare the delegate / datasource methods
--------------------- CHILD VIEW CONTROLLER ---------------------
#interface ViewController : FatherViewController
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate=self;
self.tableView.dataSource=self;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [super numberOfSectionsInTableView:tableView];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [super tableView:tableView numberOfRowsInSection:section];
}
Is there anyway to control UIButton state (enable/disable button) in UITableViewCell. My problem is my UIButton in cell is made in storyboard using viewWithTag. I've been spending quite a lot of time to sort it out but no luck. People mostly sort out the problem by programmatically assigning the tag for the button with cell indexPath.
I'm aware of that the table will reuse the cell, but I just want to ask if there is another hacky way to sort out my issue. If it is impossible, I might have to create the button programmatically.
You could loop through all the subviews of the cell and check if they are a UIButton using isMemberOfClass to get your button. If you have multiple buttons you could then check the text of the button or some other property that uniquely identifies it. That would be a hacky way to do it.
You have to make a custom cell like that:
CustomCell.h
#protocol CustomCellDelegate <NSObject>
- (void)buttonPressed:(UIButton *)sender;
#end
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
#property (weak, nonatomic) id<CustomCellDelegate> delegate;
#property (weak, nonatomic) IBOutlet UIButton *button;
- (IBAction)buttonPressed:(UIButton *)sender;
#end
CustomCell.m
#import "CustomCell.h"
#implementation CustomCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)prepareForReuse{
self.button.enable = YES;
}
- (IBAction)buttonPressed:(UIButton *)sender{
[self.delegate buttonPressed:sender];
}
#end
after in IB you add a new UITableViewCell at your UITableView and the class of it with you new custom cell set the Identify ID like "CustomCell" add your Button to your custom cell and connect the Outlet, then you modify you tableView:cellForRowAtIndexPath: like that:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier=#"CustomCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.delegate = self;
return cell;
}
- (void)buttonPressed:(UIButton *)sender{
sender.enable = NO;
}
Also you have to add the CustomCellDelegate in your controller's heater file
One simple way would be to keep a NSMutableArray variable in your viewcontroller and with that keep track of what cells button is disabled/enabled. And use the UITableViewDataDelegate method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
to set the buttons state each time it gets displayed. And UITableViewDelegate method:
– tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)tableViewCell forRowAtIndexPath:(NSIndexPath *)indexPath
to write to the array. Indexing with indexPath.
I'm fairly new to iOS development and I've been wrestling with a solution for this for about a day now and can't figure out why it is not working. I am trying to use a tableview within a viewcontroller as a small menu for the user to use. I have checked to see if the NSArray is being populated, and it is. I have also checked to see if the cell is being created, and it is. I just can't figure why it is not populating the tableview with the cells it creates. Below is my the code that I have so far. Thanks in advance for any help anyone can provide.
MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *menuTableView;
#property (weak, nonatomic) IBOutlet UIButton *menuButton;
#property (strong, nonatomic) NSArray *menuItemsArray;
#property (weak, nonatomic) IBOutlet UILabel *menuLabel;
#end
MainViewController.m
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize menuItemsArray, menuTableView, menuButton, menuLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Set TableView Delegate/DataSource to self
[self.menuTableView setDelegate:self];
[self.menuTableView setDataSource:self];
[self.menuTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.menuTableView setBounces:NO];
[self.menuTableView setRowHeight:self.menuLabel.frame.size.height];
self.menuItemsArray = [[NSArray alloc] initWithObjects:#"Add Category", #"Add Item", #"Settings", nil];
NSLog(#"array: %#", menuItemsArray);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#pragma mark - UITableViewDelegate
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return ([self.menuItemsArray count]);
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"menuCell"];
[[cell textLabel]setText:[self.menuItemsArray objectAtIndex:indexPath.row]];
[[cell textLabel]setFont:[self.menuLabel font]];
return cell;
}
-(void)tableView:(UITableView *)tableview didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.menuTableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *selectedString = [self.menuItemsArray objectAtIndex:indexPath.row];
self.menuLabel.text = selectedString;
}
I had the same problem, my table view was not getting displayed within a view controller.
I have found a solution.
You can create another view controller with a Container view on it. And put ur table view on a Table View controller. just embed the table view controller to the container view of ur mail view controller.
Make sure your initWithNib method is being called. If you are calling [[MainController alloc] init] your "menuTableView" will never be created from the Nib. Also, double-check the table view by setting the backgroundColor of the main table view to [UIColor red] or something just to make sure the tableView is present and that it has the frame you expect. It might be sitting behind one of your other views, have a frame of (0,0,0,0), or not be present in the view at all.
Also try calling [self.menuTableView reloadData] at the end of your 'viewDidLoad' or initialize the menuItemsArray before you set the data source and delegate (i.e. in your initWithNib method).
And when you do get it all working (you are very close) you will want to change your cellForRow method to something more like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:#"menuCell"];
if(!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"menuCell"];
}
[[cell textLabel]setText:[self.menuItemsArray objectAtIndex:indexPath.row]];
[[cell textLabel]setFont:[self.menuLabel font]];
return cell;
}
This will allow you to take advantage of the cell reuse that makes table views so efficient.
This is a bit late as you have found a way around it but I was having the same problem as you and found that I needed to connect the IBOutlet property to the table view in storyboard and then it all worked.
I hope this helps you in future.
One cause of the symptoms described is if you have placed the UITableView in the parent view using a container view in a storyboard, but are initialising and populating in code a different instance of the UITableView than the one that is actually being presented to the user. If you have placed the UITableView within the view using a container view, then you need to do the following:
Connect the UITableView to the container view with a segue, by Control-Dragging from the container view to the UITableView in the Storyboard.
Click on the segue, and give it a name e.g. tableViewSegue.
Set up the table by implementing prepareForSegue:sender:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString * segueName = segue.identifier;
if ([segueName isEqualToString: #"tableViewSegue"]) {
UIViewController * myTableView = [segue destinationViewController];
// Do any table setup here, such as injecting the data array into a property on the tableView.
}
}
If instead you have been creating a different UITableView in code, what you will see is an unpopulated UITableView that follows the specifications set up in the storyboard (e.g., row height spacing will be correct) and which is responding to user interaction, but is empty. The empty one is the one being initialised automatically for you by the storyboard, and meanwhile you've been creating another UITableView somewhere else:
// DON'T DO IT THIS WAY IF YOU'RE USING STORYBOARD.
- (void)viewDidLoad {
[super viewDidLoad];
// Incorrectly creating a tableview child table view that won't be the one presented.
self.myTableView = [MYTableViewClass new];
// ...further configuration of the table.
}
If you follow this incorrect path, the other UITableView you are creating is being built in memory, and populated with your data array, so you will see all the NSLog statements from that process and be able to see a UITableView in memory with the correct number of objects and so on as you step through the executing code, but what is hard to pick up is you're not looking at the one being presented to the user. So can be tricky to track down. :)
Just remove the code above, implement prepareForSegue:sender: and the universe will return to being a predictable place.
If you add UITableView inside the UIViewController, you need to set the frame size of the UITableView same as the frame size of the view inside the UIViewController, otherwise the tableview size may be 0, cannot display anything.
You can set the frame size if you create the UITableView by storyboard in your case:
- (void)viewDidLoad {
[super viewDidLoad];
// Set tableview delegate and datasource here
menuTableView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
I have a UITableViewController that displays a custom UITableViewCell (inherited from UITableViewCell). That same UITableViewCell has a UILabel that can have text of variable length. The challenge I am having is how to access this UILabel in my UITableViewController so that I can set the correct cell height in heightForRowAtIndexPath.
Or on a side note how do I solve my problem of having a dynamicically sized label.
thanks
Here is my code for the custom UITableViewCell:
header:
#import <UIKit/UIKit.h>
#interface MessagesCustomViewCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *message; <---- need to access this
#end
implementation:
#import "MessagesCustomViewCell.h"
#implementation MessagesCustomViewCell
#synthesize message=_message;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
Using the way of measuring the font size mentioned by Ivan, you could additionally add a class metod to MessagesCustomViewCell,
+ (CGFloat)heightForMessage:(NSString *)message;
in which you calculate the height of message using the appropriate UILabel width/height, font etc. This could be called from heightForRowAtIndexPath: as such:
NSString *dynamicString = [self.mydata objectAtIndex:indexPath.row];
CGFloat height = [MessagesCustomViewCell heightForMessage:dynamicString];
return height;
Call [tableView reloadData] once data is updated.
Check the size with [yourString sizeWithFont:(UIFont*) forWidth(CGFloat) lineBreakMode:(NSLineBreakMode)] method in heightForRowAtIndex. The referred method will return a required size (including the height).