I've created a custom UITableViewCell, but when I run my app on iPad the cell's content is the same width as on iPhone. I want the content - the background UIView for example - to be the complete width.
customCell.h
#import <UIKit/UIKit.h>
#interface customCell : UITableViewCell
#property (nonatomic, strong) UILabel *title;
#property (nonatomic, strong) UILabel *description;
#property (nonatomic, strong) UIView *background;
#end
customCell.m
#import "customCell.h"
#implementation customCell
#synthesize title = _title;
#synthesize description = _description;
#synthesize background = _background;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// configure background
self.background = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.contentView.frame.size.width, 115.0f)];
// configure title
self.title = [[UILabel alloc] initWithFrame:CGRectMake(5, 10, self.contentView.frame.size.width-10, 70)];
self.title.textAlignment = NSTextAlignmentNatural;
self.title.lineBreakMode = NSLineBreakByWordWrapping;
self.title.numberOfLines = 4;
self.title.preferredMaxLayoutWidth = 20;
self.title.adjustsFontSizeToFitWidth = NO;
self.title.textColor = [UIColor whiteColor];
self.title.font = [UIFont fontWithName:#"SourceSansPro-Regular" size:16];
[self.background addSubview:self.title];
// configure description
self.description = [[UILabel alloc] initWithFrame:CGRectMake(5, 80, self.contentView.frame.size.width-10, 20)];
self.description.textColor = [UIColor whiteColor];
self.description.textAlignment = NSTextAlignmentLeft;
self.description.font = [UIFont fontWithName:#"SourceSansPro-Bold" size:13];
[self.background addSubview:self.description];
[self addSubview:self.background];
[self sendSubviewToBack:self.background];
}
return self;
}
#end
What am I doing wrong here?
The cell's size is not yet set in the initWithStyle:reuseIdentifier: method. Either setup your subviews with proper autoresizingMask values or implement the cell's layoutSubviews method to update their sizes.
Also, since you add the title and description labels to the background view, the labels' sizes should be based on the size of the background view, not the size of the cell's contentView.
And lastly, never name a property description. It will conflict with the description method inherited from NSObject.
Related
I'm working with iOS8 Self-Sizing cell and I want the cell resize to fit its subview, messageBodyButton and profileImageView.
profileImageView's size is fixed. As you can see in the picture below, the messageBodyButton doesn't resize to fit its titleLabel.
Why did this happen? How to fix this bug using Auto Layout?
And when the titleLabel's content changed(say, the model's data changed), how should I update constraints so that the cell height can be calculated correctly?
Here is my code:
RXTChatCell.m:
#property (nonatomic, strong) UILabel *timeLabel;
#property (nonatomic, strong) UIImageView *profileImageView;
#property (nonatomic, strong) UIButton *messageBodyButton;
#property (nonatomic, assign) BOOL needUpdateConstraints;
#property (nonatomic, assign) BOOL didSetUpConstraints;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_timeLabel = [[UILabel alloc] init];
_timeLabel.text = #"19:00";
[self.contentView addSubview:_timeLabel];
_profileImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
[self.contentView addSubview:_profileImageView];
_messageBodyButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_messageBodyButton.titleLabel setLineBreakMode:NSLineBreakByTruncatingTail];
[_messageBodyButton.titleLabel setTextAlignment:NSTextAlignmentRight];
_messageBodyButton.titleLabel.numberOfLines = 0;
_messageBodyButton.titleLabel.backgroundColor = UIColor.grayColor;
[self.contentView addSubview:_messageBodyButton];
_timeLabel.backgroundColor = UIColor.redColor;
_profileImageView.backgroundColor = UIColor.greenColor;
_messageBodyButton.backgroundColor = UIColor.blueColor;
self.contentView.backgroundColor = UIColor.orangeColor;
}
return self;
}
- (void)updateConstraints
{
if (!self.didSetUpConstraints) { // set up constraints
[_timeLabel makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.contentView.topMargin);
make.centerX.equalTo(self.contentView);
}];
[_profileImageView makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(_timeLabel.bottom).offset(10);
make.right.equalTo(self.contentView).offset(-10);
make.width.and.height.equalTo(50);
make.bottom.lessThanOrEqualTo(self.contentView.bottomMargin);
}];
[_messageBodyButton makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(_profileImageView);
make.right.equalTo(_profileImageView.left).offset(-10);
make.left.equalTo(self.contentView).offset(10);
make.bottom.lessThanOrEqualTo(self.contentView.bottomMargin);
}];
self.didSetUpConstraints = YES;
}
if (self.needUpdateConstraints){
// here, how should I update constraints?
}
[super updateConstraints];
}
- (void)setMessage:(RXTChatMessage *)message
{
EMTextMessageBody *body = (EMTextMessageBody *)message.messageBody.body;
[self.messageBodyButton setTitle:body.text forState:UIControlStateNormal];
self.needUpdateConstraints = YES;
[self setNeedsUpdateConstraints];
}
RXTChatViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.estimatedRowHeight = 44;
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RXTChatMessage *message = self.messages[indexPath.row];
RXTChatCell *cell = [tableView dequeueReusableCellWithIdentifier:RXTChatCellId];
if (cell == nil) {
cell = [[self alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RXTChatCellId];
}
cell.message = message;
return cell;
}
Select Your Button -> Go to Editor -> Size to fit content.
Remove fixed height constraint for your button (only set leading,trailing,top bottom) constraint (Superview should be your cell view).
This should solve your problem
let me know if it solves your issue.
I have customized a tableview cell programmatically.
header file:
#import <UIKit/UIKit.h>
#class LoadingCell;
typedef NS_ENUM(NSUInteger, LoadingCellStyle) {
LoadingCellStyleSelection,
LoadingCellStyleTextField,
LoadingCellStyleImagePicker,
};
#interface LoadingCell : UITableViewCell
#property (nonatomic, strong) UIImageView* leftImageView;
#property (nonatomic, strong) UILabel* titleLabel;
#property (nonatomic, strong) UILabel* detailLabel;
#property (nonatomic, assign) LoadingCellStyle style;
-(instancetype)initWithStyle:(LoadingCellStyle)style;
#end
implementation file:
#import "LoadingCell.h"
#define itemHeight 44
#implementation LoadingCell
-(instancetype)initWithStyle:(LoadingCellStyle)style
{
self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"loadingStats"];
self.style = style;
[self.contentView addSubview:self.titleLabel];
[self.contentView addSubview:self.leftImageView];
if (self.style == LoadingCellStyleSelection) {
}
return self;
}
#pragma mark property setter
-(UILabel *)titleLabel
{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = COLOR_TEXT_GRAY;
_titleLabel.font = [UIFont systemFontOfSize:13];
}
return _titleLabel;
}
-(UILabel *)detailLabel
{
if (!_detailLabel) {
_detailLabel = [[UILabel alloc] init];
_detailLabel.textColor = COLOR_TEXT_GRAY;
}
return _detailLabel;
}
-(UIImageView *)leftImageView
{
if (!_leftImageView) {
_leftImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"star"]];
}
return _leftImageView;
}
#pragma mark layouts
-(void)layoutSubviews
{
CGRect imageframe = CGRectMake(k_Margin, 0, itemHeight, itemHeight);
[self.leftImageView setFrame:imageframe];
[self.titleLabel setFrame:CGRectMake(CGRectGetMaxX(imageframe), 0, 100, itemHeight)];
[self.detailLabel setFrame:CGRectMake(CGRectGetMaxX(self.titleLabel.frame), 0, kScreen_Width-CGRectGetMaxX(self.titleLabel.frame)-50, itemHeight)];
}
#end
And the result is shown like
As you see, the cell separator is short on the right side.
self.separatorInset = UIEdgeInsetsMake(0, 0, 0, -100);
this method is able to work around but I think there is mistakes on my method to customizing the tableview cell.
Thank you
Solution: I missed
[super layoutSubviews];
inside my customized the layout methods.
Disclaimer: I'm new comparably new to iOS development. I'm using ARC for this project
I have a very simple custom UITableViewCell
UITableViewListRightAlignedCell.h
#interface UITableViewListRightAlignedCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *lblTitle;
#property (weak, nonatomic) IBOutlet UIImageView *imgIcon;
#end
UITableViewListRightAlignedCell.m
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
[self initalize];
}
return self;
}
-(void)awakeFromNib {
[super awakeFromNib];
[self initalize];
}
-(void) initalize {
self.lblTitle.backgroundColor = [UIColor clearColor];
self.lblTitle.textAlignment = UITextAlignmentRight;
self.lblTitle.font = [UIFont fontWithName:FONTRobotoRegular size:16];
self.lblTitle.textColor = RGBColor(0x5D350BFF);
}
This custom cell works fine when I have a cell in my storyboard and I connect UILabel to lblTitle and an UIImage to imgIcon.
However, I want to use the same custom cell class somewhere else in the code with a programmatically created UITableView. The problem is that the properties lblTitle and imgIcon are not being set because they are not initialized and I can't initialize them because them because are they are outlets and weak.
I need to know what's the correct approach in this case.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self createMyLabel:nil];// create your label here
// Initialization code
[self initalize];
}
return self;
}
-(void)awakeFromNib {
[super awakeFromNib];
[self initalize];
}
-(void) initalize {
//self.lblTitle.backgroundColor = [UIColor clearColor];
self.lblTitle.textAlignment = UITextAlignmentRight;
self.lblTitle.font = [UIFont fontWithName:FONTRobotoRegular size:16];
self.lblTitle.textColor = RGBColor(0x5D350BFF);
}
-(void)createMyLabel:(id)sender{
//Create your label
UILabel *MyLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 100)];
//Set background color for debugging
[storeAddress setBackgroundColor: [UIColor blueColor]];
//set this created label as your lblTitle label
[self setLblTitle:MyLabel];
//Add it to the cell's content view
[[self contentView] addSubview:[self lblTitle]];
//Then do something with that
//[self initalize];
//[MyLabel release]; // For Non ARC
}
Each table view cell contains contentView. This view is what you see in IB. When you add controls to it in IB (UILabel in your case), you can use them as weak because this contentView already has strong reference to it. So what you need:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[label setText:#"Test"];
[cell.contentView addSubview:label];
[cell setTitleLabel:label];
You have to know that when using a Storyboard, only awakeFromNib is called. initWithStyle is called only in the case of a programmatical initialization by another class.
Therefore, you are correct when using this kind of code :
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
[self initalize];
}
return self;
}
-(void)awakeFromNib {
[super awakeFromNib];
[self initalize];
}
-(void) initalize {
// initialization code
}
This is exactly what Paul Hegarty shows in the Stanford iOS course. It ensures that whatever the caller, your class is initialized in a consistent way.
So, to answer your question, if you want to use this class programmatically, add this kind of code ONLY in the initWithStyle method (since it's the one being called programmatically) :
_lblTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.contentView addSubview:_lblTitle];
Same thing for your other property.
Of course, you don't add that to the awakeFromNib method, since your outlets will already be initialized by the storyboard itself.
Regards,
Fred
I have this code segment:
if (cell == nil)
{
CGRect cellFrame = CGRectMake(0,0,300,250);
cell = [[UITableViewCell alloc] initWithFrame:cellFrame
reuseIdentifier:CellTableIndetifier];
CGRect nameLabelRect = CGRectMake(0, 5, 70, 20);
UILabel* nameLabel = [[UILabel alloc] initWithFrame:nameLabelRect];
nameLabel.textAlignment = NSTextAlignmentCenter;
nameLabel.text = #"Name";
nameLabel.font = [UIFont boldSystemFontOfSize:12];
[cell.contentView addSubview: nameLabel];
CGRect colorLabelRect = CGRectMake(0, 25, 70, 20);
UILabel* colorLabel = [[UILabel alloc] initWithFrame:colorLabelRect];
colorLabel.textAlignment = NSTextAlignmentCenter;
colorLabel.text = #"Color";
colorLabel.font = [UIFont boldSystemFontOfSize:12];
[cell.contentView addSubview: colorLabel];
CGRect priceLabelRect = CGRectMake(0, 45, 70, 20);
UILabel *priceLabel = [[UILabel alloc] initWithFrame:priceLabelRect];
priceLabel.text = #"Price";
priceLabel.textAlignment = NSTextAlignmentCenter;
colorLabel.font = [UIFont boldSystemFontOfSize:12];
[cell.contentView addSubview:priceLabel];
CGRect nameValueRect = CGRectMake(80, 5, 200, 20);
UILabel* nameValue = [[UILabel alloc] initWithFrame: nameValueRect];
nameValue.tag = kNameValueTag;
[cell.contentView addSubview:nameValue];
CGRect colorValueRect = CGRectMake(80, 25, 200, 20);
UILabel* colorValue = [[UILabel alloc] initWithFrame:colorValueRect];
colorValue.tag = kColorValueTag;
[cell.contentView addSubview:colorValue];
CGRect priceValueRect = CGRectMake(80, 45, 200, 20);
UILabel *priceValue = [[UILabel alloc] initWithFrame:priceValueRect];
priceValue.tag = kPriceValueTag;
[cell.contentView addSubview:priceValue];
}
and I would like to make that make that into a subclass, so I don't have to write all those lines, I just say cell = CustomCell and it does everything in the subclass.
Here is the basic code for a subclass of UITableCellView :
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
{
}
#end
-----------------------------------------------------------
#import "CustomCell.h"
#implementation CustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
}
/*
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}*/
#end
It is auto-generated if you create a new file of type Objective-C Class and specify UITableViewCell in filed subclass of
Following is what I usually do. If you use the cell only in 1 view controller, you can just put it in the same file as the view controller.
#interface MyCell : UITableViewCell
#property (strong, nonatomic) UILabel* nameValue;
#property (strong, nonatomic) UILabel* colorValue;
#property (strong, nonatomic) UILabel* priceValue;
#end
#implementation MyCell
-(id)init {
self = [super initWithStyle:whatever_style];
// Create & position UI elements
UILabel* nameLabel = [[UILabel alloc] init];
nameLabel.frame = .... // frame, font, etc
[self.contentView addSubview:nameLabel]
self.nameValue = [[UILabel alloc] init];
self.nameValue = .... // frame, font, etc
[self.contentView addSubview:self.nameValue];
// Do the same thing for color, price
return self;
}
#end
By exposing nameValue, colorValue, priceValue, I allow them to be changed from outside (ie the UITableViewController). I didn't expose other labels because they are static. Unless you need special positioning, you don't have to override layoutSubviews. autoresizingMask is sufficient in most cases.
There is two way I use to solve this.
The "quick and dirty" is to design a UITableViewCell into your UITableView with the stuff you need (UILabel, UIImageView,...) and set a unique tag for each element, then when you dequeue a UITableViewCell you can reuse the elements in like this :
UILabel *nameLabel = (UILabel*)[cell viewWithTag:NAME_LABEL_TAG];
if(!nameLabel) {
// If the label does not exist, create it
CGRect nameLabelRect = CGRectMake(0, 5, 70, 20);
nameLabel = [[UILabel alloc] initWithFrame:nameLabelRect];
nameLabel.textAlignment = NSTextAlignmentCenter;
nameLabel.text = #"Name";
nameLabel.font = [UIFont boldSystemFontOfSize:12];
[cell.contentView addSubview: nameLabel];
}
Or the (imo) best way is to create a custom UITableViewCell and subclass UItableviewCell, you have a good tutorial there : Custom UITableViewCell
I guess your are putting this stuff in your cellForRowAtIndexPath: delegate method and I can see why your strive to remove it from this place.
Create a new Objective-C class via New->File and put the subview related calls you posted in the layoutSubviews: method. In cellForRowAtIndexPath: in your table view delegate now use this class instead of the generic UITableViewCell. Don't forget to import your newly created file.
#import "CellVideo.h"
#implementation CellVideo
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
NSLog(#"initWithCoder");
self = [super initWithCoder: aDecoder];
if (self)
{
// Initialization code
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] init];
[moviePlayer.view setFrame:CGRectMake(10, 75, 300, 260)];
[moviePlayer.view setBackgroundColor:[UIColor blackColor]];
[moviePlayer.view setTag:333];
[moviePlayer setControlStyle:MPMovieControlStyleNone];
moviePlayer.scalingMode = MPMovieScalingModeFill;
_movie=moviePlayer;
UIImageView *imagrViewThumb=[[UIImageView alloc]initWithFrame:CGRectMake(10, 75, 300, 260)];
[imagrViewThumb setBackgroundColor:[UIColor redColor]];
[imagrViewThumb setTag:333];
[self.contentView insertSubview:imagrViewThumb atIndex:0];
}
return self;
}
-(void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
///use it in this way
CellIdentifier=#"cellvideo";
UITableViewCell *cell=nil;
// CellVideo *cellVideo=nil;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
I'm using a standard prototype cell with a label and a detail text. I set the detail text to be two lines and set the text to something with a line break. This works fine - however, the (left aligned) main label has lost its vertical centering, though I didn't change any of its attributes.
This is what it looks like in Storyboard:
"Title" should be vertically centered.
You can use a custom cell in which you can define the attributes of the custom properties.The size of the text label is not fixed. I think this is the reason why the alignment does not work.
You can subclass the UITableViewCell and do your layout in the initWithStyle method. For ex:
MyCustomCell.h file:
#interface MyCustomCell : UITableViewCell
{
UILabel *_label1;
UILabel *_label2;
UILabel *_titleLabel;
}
#property (nonatomic, strong) UILabel *label1;
#property (nonatomic, strong) UILabel *label2;
#property (nonatomic, strong) UILabel *titleLabel;
#end
MyCustomCell.m file:
#import "MyCustomCell.h"
#implementation MyCustomCell
#synthesize label1 = _label1;
#synthesize label2 = _label2;
#synthesize titleLabel = _titleLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.label1 = [[UILabel alloc]initWithFrame:CGRectMake(x, y, w, h)]; //x: x coordinate, y: y coordinate, w is width , h is height
self.label2 = [[UILabel alloc]initWithFrame:CGRectMake(x, y, w, h)];
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(x, y, w, h)];
self.titleLabel.font = [UIFont fontWithName:#"Times New Roman" size:30.0f]; //change font name and size per your needed.
[self addSubview:self.label1];
[self addSubview:self.label1];
[self addSubview:self.titleLabel];
}
return self;
}
In the class file where you have cellForRowAtIndexPath method, make sure include the #import "MyCustomCell.h" and use MyCustomCell instead of UITableViewCell to allocate the cell. And assigning data to labels as so:
cell.label1.text = xxxx;
cell.label2.text = xxxx;
cell.titleLabel.text = xxxx;