Using UICollectionView in a universal app - objective-c

I need to create a universal app with a listing. And on clicking a cell it shows the detail view of the cell. I have created the listing for a iPad by using a UICollectionView inside a UIViewController. But when I try the same in iPhone it doesn't show properly. Its kind of a zoomed version of iPad cell.
For iPad I need the cell to be like this
http://i.stack.imgur.com/8mJnI.png
And for iPhone I need the cell to be like this
http://i.stack.imgur.com/moUxm.png
What is the best way to do this?
Any help will be appreciated

I would suggest create to abstract subclass of UICollectionViewCell with properties like image, title, desc and two subclasses of the abstract class, for example iPadCell and iPhoneCell.
In the storyboard add two prototype cells and change it class and identifier to iPhoneCell and iPadCell.
Layout the cell as you need and in collectionView:cellForItemAtIndexPath: dequeue right cell, for appropriate device:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
YourAbstractClass *cell = nil;
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
// Make sure it match storyboard identifier for iPad cell
cell = [cv dequeueReusableCellWithReuseIdentifier:#"iPadCellIdentifier" forIndexPath:indexPath];
}
else { //iPhone device
cell = [cv dequeueReusableCellWithReuseIdentifier:#"iPhoneCellIdentifier" forIndexPath:indexPath];
}
cell.imageView.image = ...;
cell.title = ...;
cell.description = ...;
return cell;
}
In very similar way you can set up cell size, if it's different for iPhone/ipad:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
return CGSizeMake(400.0f, 500.0f);
}
return CGSizeMake(200.0f, 300.0f)
}

since the content of you cell is same i.e.,
ImageView
UILabel for Title
UILabel for Description
Instead of making a abstract class and then subclassing them, What you could also do is, you just make one custom class of UICollectionViewCell, for example name it as GenericCollectionViewCell, Now this class should have two nib files, and make sure you assign different reuse Identifier to both of them and different file name ofcourse, e.g.
iPhoneCell
iPadCell
So, now basically you have GenericCollectionViewCell.h with IBOutlets to two different nibs.
You will have to create a ENUM to in the GenericeCollectionViewCell e.g.
typedef NS_ENUM(NSUInteger, CellType) {
CELLIPHONE = 0,
CELLIPAD = 1
};
you will use this ENUM to initialise, What type of nib to use for this cell e.g.
+ (UINib*)cellNibForCellType:(CellType)cellType{
switch (cellType) {
case CELLIPHONE:
cellNib = [UINib nibWithNibName:#"IPhoneCollectionViewCell"
bundle:nil];
break;
case CELLIPAD:
cellNib = [UINib nibWithNibName:#"IPadCollectionViewCell"
bundle:nil];
break;
}
Once you have done that, you need to register this cell in viewDidLoad of your ViewController,
- (void)viewDidLoad
{
[super viewDidLoad];
[_collectionView registerNib:[GenericCollectionViewCell cellNibForCellType:CELLIPHONE] forCellWithReuseIdentifier:#"iPhoneCell"];
[_collectionView registerNib:[GenericCollectionViewCell cellNibForCellType:CELLIPAD] forCellWithReuseIdentifier:#"iPadCell"];
}
And then you could proceed with what, #Greg has explained above,
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
GenericCollectionViewCell *cell = nil;
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
// Make sure it match storyboard identifier for iPad cell
cell = [cv dequeueReusableCellWithReuseIdentifier:#"iPadCell" forIndexPath:indexPath];
}
else { //iPhone device
cell = [cv dequeueReusableCellWithReuseIdentifier:#"iPhoneCell" forIndexPath:indexPath];
}
cell.imageView.image = ...;
cell.title = ...;
cell.description = ...;
return cell;
}
Basically this is what I have come up with, if the content of the cells are same, what's the point of making super class, I mean I don't know I might be wrong.

In your viewDidLoad():
let layout = collectionViewLayout as! UICollectionViewFlowLayout
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Pad)
{
layout.minimumLineSpacing = 30
layout.minimumInteritemSpacing = 10
layout.itemSize = CGSize(width: 200, height: 200);
layout.scrollDirection = .Vertical
layout.sectionInset = UIEdgeInsets(top: 50, left: 20, bottom: 20, right: 20)
}else if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone){
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 2
layout.itemSize = CGSize(width: 150, height: 150)
layout.scrollDirection = .Vertical
layout.sectionInset = UIEdgeInsets(top: 50, left: 20, bottom: 20, right: 20)
}

Related

Show only the first row in a UITableView

So, most of the questions are "my tableView only shows the first row, what's wrong?".
Well, what I need is exactly the opposite.
I have a SearchBar (not Search Display Controller) and until the user starts typing, I want to show ONLY the first row and nothing more.
My TableView's content is Dynamic Prototypes, with 2 Prototype Cells.
The first is the only one I want to show, but it shows others in blank.
https://www.dropbox.com/s/q4ak6z3gbc0gh5c/Screenshot%202014-07-22%2011.24.22.png
This is my tableView:numberOfRowsInSection:.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger numberOfRows = 0;
if ([self.searchBar.text isEqualToString:#""]) {
numberOfRows = 1;
}
return numberOfRows;
}
All the help will be very appreciated! \o/
Probably there is a better solution, but as proposed before :
CGRect frame = [self.tableView frame];
frame.size.height = [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
self.tableView.frame = frame;
In your view controller's -tableView:numberOfRowsInSection: make sure tableView equals to self.tableView, and then you return 1:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.tableView) {
return 1;
} else {
return yourDataModel.count;
}
}
You must change the size UITableView's contentView or frame at the creation (alloc).
This height must be the same of an UITableViewCell by default (44px) or custom.
Write the below 4 lines to make a table with one row. If you want to extend the content of the table just make the tableview scrollable.
tblView = [[UITableView alloc]initWithFrame:CGRectZero];
tblView.separatorColor = [UIColor clearColor];
tblView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
tblView.scrollEnabled = NO;

Image of UITableViewCell is not indenting

I Have created a default UITableViewCell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Init empty cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:GroupCellIdentifier];
// Get the group info
GroupInfo *groupInfo = (GroupInfo *)[_sortedGroupInfos objectAtIndex:[indexPath row]];
// Check if we have initialized this cell before
if(cell == nil)
{
// Initialize cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:GroupCellIdentifier];
// Set the accessory type
cell.accessoryType = UITableViewCellAccessoryNone;
// Set the selection type
cell.selectionStyle = [groupInfo.groupType isEqualToString:GroupTypePersonal] ? UITableViewCellSelectionStyleDefault : UITableViewCellSelectionStyleNone;
// Set the indentation width
cell.indentationWidth = 40;
}
// Set the label enabled status depending on the group type
cell.textLabel.enabled = [groupInfo.groupType isEqualToString:GroupTypePersonal];
cell.detailTextLabel.enabled = [groupInfo.groupType isEqualToString:GroupTypePersonal];
// Set text
cell.textLabel.text = groupInfo.description;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%d %#", groupInfo.storeCount, [[Model instance] getTranslationFromSection:kSectionStoreSettings translationKey:#"Stores"]];
// Set the image depending on the group type
cell.imageView.image = [GroupInfo getImageForGroupType:groupInfo.groupType];
// Return the cell
return cell;
}
I also implemented the indentationLevelForRowAtIndexPath function:
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get the group info
GroupInfo *groupInfo = _sortedGroupInfos[[indexPath row]];
// Return the indentation
return groupInfo.indentation;
}
Now I get the following table view:
http://i40.tinypic.com/f3e63t.png
My question is: Why is the image not indenting in the table view cell?
I ended up with subclassing UITableViewCell and overriding the layoutSubViews method:
- (void)layoutSubviews
{
// Call super
[super layoutSubviews];
// Update the separator
self.separatorInset = UIEdgeInsetsMake(0, (self.indentationLevel * self.indentationWidth) + 15, 0, 0);
// Update the frame of the image view
self.imageView.frame = CGRectMake(self.imageView.frame.origin.x + (self.indentationLevel * self.indentationWidth), self.imageView.frame.origin.y, self.imageView.frame.size.width, self.imageView.frame.size.height);
// Update the frame of the text label
self.textLabel.frame = CGRectMake(self.imageView.frame.origin.x + 40, self.textLabel.frame.origin.y, self.frame.size.width - (self.imageView.frame.origin.x + 60), self.textLabel.frame.size.height);
// Update the frame of the subtitle label
self.detailTextLabel.frame = CGRectMake(self.imageView.frame.origin.x + 40, self.detailTextLabel.frame.origin.y, self.frame.size.width - (self.imageView.frame.origin.x + 60), self.detailTextLabel.frame.size.height);
}
My table view now looks like:
http://tinypic.com/r/24y20if/5
I was looking only for the image indentation and successfully implemented it in Swift in the end. Thank you!
However, there were some issues with the conversion between Int and CGFloat, The same thing in Swift worked for me like this:
class CustomUITableViewCell: UITableViewCell
{
override func layoutSubviews() {
// Call super
super.layoutSubviews();
// Update the frame of the image view
self.imageView!.frame = CGRectMake(self.imageView!.frame.origin.x + (CGFloat(self.indentationLevel) * self.indentationWidth), self.imageView!.frame.origin.y, self.imageView!.frame.size.width, self.imageView!.frame.size.height);
}
}
Roberto posted pretty nice solution.
However if layoutSubviews called multiple times during cell presentation the cell's content drive away to the right.
I was able to resolve the issue by adding
static CGFloat imageViewXOrigin = 0;
if(imageViewXOrigin == 0)
imageViewXOrigin = self.imageView.frame.origin.x;
before frames calculation calls, and substituting every
self.imageView.frame.origin.x
to
imageViewXOrigin
variable.

Dynamically size uitableViewCell according to UILabel (With paragraph spacing)

I have a UITableView which is populated by text and images from a JSON file. The TableView Cell is currently sizing correctly for "posts" that do not contain many line breaks in the text however I cannot get it to calculate the correct height for "posts" with 4 or 5 line breaks.
Code for getting height:
-(float)height :(NSMutableAttributedString*)string
{
NSString *stringToSize = [NSString stringWithFormat:#"%#", string];
CGSize constraint = CGSizeMake(LABEL_WIDTH - (LABEL_MARGIN *2), 2000.f);
CGSize size = [stringToSize sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:contraint lineBreakMode:NSLineBreakByWordWrapping];
return size.height;
}
How do I calculate the correct size while allowing for line breaks and white space?
EDIT
The Rest of the method,
Inside of TableView CellForRow:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *row = [NSString stringWithFormat:#"%i", indexPath.row];
float postTextHeight = [self height:postText];
NSString *height = [NSString stringWithFormat:#"%f", heightOfPostText + 70];
[_cellSizes setObject:height forKey:row];
}
And the height of Table Cell:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *imageHeightString = [NSString stringWithFormat:#"%#", [_cellSizes objectForKey:indexPath.row]];
float heightOfCell = [imageHeightString floatValue];
if (heightOfCell == 0) {
return 217;
};
return heightOfCell + 5;
}
better u need to calculate the height first, don't include the height calculation part in method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Better to calculate it in method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
since u are getting the data from json it is easy for u to calculate
in the "heightForRowAtIndexPath" method.
follwing code will give the example to calculate height of text change it ur requirement.
hopee this helps u :)
// i am using an array
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIFont *labelFont = [UIFont fontWithName:#"Noteworthy-Bold" size:20];
NSDictionary *arialdict = [NSDictionary dictionaryWithObject:labelFont forKey:NSFontAttributeName];
NSMutableAttributedString *message = [[NSMutableAttributedString alloc] initWithString:#"this is just the sample example of how to calculate the dynamic height for tableview cell which is of around 7 to 8 lines. you will need to set the height of this string first, not seems to be calculated in cellForRowAtIndexPath method." attributes:arialdict];
array = [NSMutableArray arrayWithObjects:message, nil];
NSMutableAttributedString *message_1 = [[NSMutableAttributedString alloc] initWithString:#"you will need to set the height of this string first, not seems to be calculated in cellForRowAtIndexPath method." attributes:arialdict];
[array addObject:message_1];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *Cell = [self.aTableView dequeueReusableCellWithIdentifier:#"cell"];
if(Cell == nil)
{
Cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
//dont include the height calculation part hear, becz heights are already set for all the cell
[Cell.textLabel sizeToFit];
Cell.textLabel.attributedText = [array objectAtIndex:indexPath.row]; // dont calculate height hear it will be called after "heightForRowAtIndexPath" method
Cell.textLabel.numberOfLines = 8;
return Cell;
}
// put ur height calculation method i took some hardcoded values change it :)
-(float)height :(NSMutableAttributedString*)string
{
/*
NSString *stringToSize = [NSString stringWithFormat:#"%#", string];
// CGSize constraint = CGSizeMake(LABEL_WIDTH - (LABEL_MARGIN *2), 2000.f);
CGSize maxSize = CGSizeMake(280, MAXFLOAT);//set max height //set the constant width, hear MAXFLOAT gives the maximum height
CGSize size = [stringToSize sizeWithFont:[UIFont systemFontOfSize:20.0f] constrainedToSize:maxSize lineBreakMode:NSLineBreakByWordWrapping];
return size.height; //finally u get the correct height
*/
//commenting the above code because "sizeWithFont: constrainedToSize:maxSize: lineBreakMode: " has been deprecated to avoid above code use below
NSAttributedString *attributedText = string;
CGRect rect = [attributedText boundingRectWithSize:(CGSize){225, MAXFLOAT}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];//you need to specify the some width, height will be calculated
CGSize requiredSize = rect.size;
return requiredSize.height; //finally u return your height
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//whatever the height u need to calculate calculate hear only
CGFloat heightOfcell = [self height:[array objectAtIndex:indexPath.row]];
NSLog(#"%f",heightOfcell);
return heightOfcell;
}
Hope this helps u :)
For SWIFT version
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate
{
var messageArray:[String] = [] //array to holde the response form son for example
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
messageArray = ["One of the most interesting features of Newsstand is that once an asset downloading has started it will continue even if the application is suspended (that is: not running but still in memory) or it is terminated. Of course during while your app is suspended it will not receive any status update but it will be woken up in the background",
"In case that app has been terminated while downloading was in progress, the situation is different. Infact in the event of a finished downloading the app can not be simply woken up and the connection delegate finish download method called, as when an app is terminated its App delegate object doesn’t exist anymore. In such case the system will relaunch the app in the background.",
" If defined, this key will contain the array of all asset identifiers that caused the launch. From my tests it doesn’t seem this check is really required if you reconnect the pending downloading as explained in the next paragraph.",
]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return messageArray.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell;
if(cell == nil)
{
cell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier: "CELL")
cell?.selectionStyle = UITableViewCellSelectionStyle.None
}
cell?.textLabel.font = UIFont.systemFontOfSize(15.0)
cell?.textLabel.sizeToFit()
cell?.textLabel.text = messageArray[indexPath.row]
cell?.textLabel.numberOfLines = 0
return cell!;
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
var height:CGFloat = self.calculateHeightForString(messageArray[indexPath.row])
return height + 70.0
}
func calculateHeightForString(inString:String) -> CGFloat
{
var messageString = inString
var attributes = [UIFont(): UIFont.systemFontOfSize(15.0)]
var attrString:NSAttributedString? = NSAttributedString(string: messageString, attributes: attributes)
var rect:CGRect = attrString!.boundingRectWithSize(CGSizeMake(300.0,CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil )
var requredSize:CGRect = rect
return requredSize.height //to include button's in your tableview
}
}
#Shan had a good answer but it didn't entirely worked for me.
This is the code I used for calculating the cell height
-(float)height :(NSMutableAttributedString*)string
{
CGRect rect = [string boundingRectWithSize:(CGSize){table.frame.size.width - 110, MAXFLOAT} options:NSStringDrawingUsesLineFragmentOrigin context:nil];
return rect.size.height;
}
I do the -110 because that will give equal space at sides top and bottom.
Hope this helps.
Implement this table view delegate method:
-tableView:heightForRowAtIndexPath:
https://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:heightForRowAtIndexPath:
You'll call your method for determining the height and return that value with some extra padding if you wanted.
The approach I recommend is to set the text of an actual label and get the required height by calling sizeToFit. For this to work, you've got to set the label's numberOfLines property to 0 and set the desired maximum width.
When using this technique with table views, you can use the prototype cell method discussed here to calculate height using an actual cell.

How do I change the color or a NSTableCellView for each cell of an NSOutlineView

I've looked at a number of questions but can't find a good solution for a View-Based NSOutlineView
Coloring NSTableView Text per row
Change color of NSTableViewCell
Custom background colors for NSTableCellView
I'm trying to set each row to whatever color I want. I've read somewhere that I need to subclass NSTableRowView which I've now done.
According to the AppleDocs, I see the following methods:
– drawBackgroundInRect:
– drawDraggingDestinationFeedbackInRect:
– drawSelectionInRect:
– drawSeparatorInRect:
How would I go about setting the background color for the individual rows? Am I going the wrong route above?
Edit: below (also edited title)
Since i'm using an NSOutlineView and not a NSTableView, when i change the background color of the cells the image looks like the following. The disclosure arrows to the left is not colored. Is there any way to change the color of the whole row for the NSOutlineView?
You could subclass NSTableViewCell, and add a method to it which sets its color.
NSTableViewCell is already a subclass of NSView, so in your subclass, you would add the following method:
- (void)setBackgroundColor {
self.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 1.0f); // or whatever color
}
Or something like that. You'll probably want to have the color be a param to the method. Then, in your table view delegate, you can set the color depending on the row index passed to the delegate method. For example:
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
if (indexPath.row % 2) {
[cell setBackgroundColor:[UIColor redColor]]; // or something like that
}
}
Came up with a solution. Implemented the following.
-(void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
[self updateRowViewBackColorforItem:[outlineView itemAtRow:row]];
}
-(void)updateRowViewBackColorforStep:(myCustomItem *)customItem {
static NSColor *color1;
static NSColor *color2;
static NSColor *color3;
if (color1 == nil) {
sharedcolorHeader = [NSColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0];
}
if (color2 == nil) {
sharedcolorChildren = [NSColor colorWithCalibratedRed:(x/255.0f) green:(y/255.0f) blue:(z/255.0f) alpha:1.0];
}
if (color3 == nil) {
normalColor = [NSColor colorWithCalibratedRed:(255/255.0f) green:(255/255.0f) blue:(255/255.0f) alpha:1.0];
}
NSInteger row = [stepOutlineView rowForItem:step];
if (row < 0) return;
NSTableRowView *view = [myOutlineView rowViewAtRow:row makeIfNecessary:NO];
if ([customItem type] == 1) {
[view setBackgroundColor:sharedcolorHeader];
} else if([customItem type] == 2) {
[view setBackgroundColor:sharedcolorChildren];
} else {
[view setBackgroundColor:normalColor];
}
}
This is really something that should rely on properties or ivars in your data model.
If you use view based outline views, you can simply have custom views for your row views and or cell views.
Have the custom views draw whatever you want based in the data in your represented object.

How can I customize the accessory disclosure image in a UITableViewCell?

I would like to use a custom version of the standard disclosure accessory image in my UITableView. How can I do this? I'm hoping that sub-classing UITableViewCell is not necessary for something this basic.
You'll need to create a custom view and assign it to the accessoryView property of the UITableViewCell object. Something like:
myCell.accessoryView = [[ UIImageView alloc ]
initWithImage:[UIImage imageNamed:#"Something" ]];
- (UITableViewCell *)tableView:(UITableView *)tView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImage *indicatorImage = [UIImage imageNamed:#"Arrow.png"];
cell.accessoryView =[[[UIImageView alloc] initWithImage:indicatorImage] autorelease];
return cell;
}
well i do this with the help code given above
I ran into the same problem as Greg--the accessory view doesn't track (if you use an UIImageView)
I solved it like this:
UIImage * image = [ UIImage imageNamed:#"disclosure-button-grey.png" ] ;
UIControl * c = [ [ UIControl alloc ] initWithFrame:(CGRect){ CGPointZero, image.size } ] ;
c.layer.contents = (id)image.CGImage ;
[ c addTarget:self action:#selector( accessoryTapped: ) forControlEvents:UIControlEventTouchUpInside ] ;
cell.accessoryView = c ;
[ c release ] ;
There is a nice example from Apple showing how to use UIControl to fulfill this kind of accessoryView customisation.
Overriding - (void)drawRect:(CGRect)rect in UIControl is not the easiest way, but gives you lots of flexibility to style nice accessory views.
Swift 4 & 5:
This worked for me:
class MyCell: UITableViewCell {
// boilerplate...
fileprivate func commonInit() {
// This is the button we want, just need to change the image.
accessoryType = .detailButton
}
open override func layoutSubviews() {
super.layoutSubviews()
// Grab the "detail" button to change its icon. Functionality is set in the delegate.
if let submitButton = allSubviews.compactMap({ $0 as? UIButton }).first {
submitButton.setImage(#imageLiteral(resourceName: "icons8-enter-1"), for: .normal)
}
}
// everything else...
}
Best of all, this lets me use tableView(_:accessoryButtonTappedForRowWith:) to handle the button actions.
I used this for the button, but the same technique works for the disclosure image, [since/so long as] there is only one class in that branch of the hierarchy tree of the element you want to touch.
Oh, right, my code calls this:
extension UIView {
var allSubviews: [UIView] {
return subviews.flatMap { [$0] + $0.allSubviews }
}
}
I'd do it as follows:
UIImageView *chevronImgVw = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"chevron_accessory_vw_img.png"]];
chevronImgVw.frame = CGRectMake(cell.accessoryView.frame.origin.x, cell.accessoryView.frame.origin.y, 10, 20);
cell.accessoryView = chevronImgVw;
In swift 4 & 5
myCell.accessoryView = UIImageView(image: UIImage(named: "Something"))
NOTE: Do NOT set a custom cell to tag:0. That tag is reserved for the textLabel view. If you set tags in Xcode Storyboard, you must set tags for custom views/labels to 1 or higher.
Note Apple's example:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
UILabel *label;
label = (UILabel *)[cell viewWithTag:1];
label.text = [NSString stringWithFormat:#"%d", indexPath.row];
label = (UILabel *)[cell viewWithTag:2];
label.text = [NSString stringWithFormat:#"%d", NUMBER_OF_ROWS - indexPath.row];
return cell;
}
I tried the above code and it doesn't seem to work anymore, maybe due to the age of this post so I'll throw down my line that gave me the customized accessory view.
[cell setAccessoryView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrowBtn"]]];
Hope someone finds this useful.