UITableView content of cell dont move on editing - objective-c

I have a UITableView with some custom cells. In each cell, there is a ImageView and three labels and get the data from a string array. I have done the layout in my storyboard. The data source is a string array. This works.
Now I have insert a EditButton in the code. Now i can see the EditButton, but when I activate the edit mode the table cell will be resized, but the images and labels dont move.
Can you show me how to move the content of the cell? Who knows a tutorial with UITableView uses EditMode AND storyboards. All tutorials which I have found are based on the "old" Xcode.
Thank you very much
By the way, here is my code:
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
myData = [NSMutableArray arrayWithObjects:
#"Line1_Label1|Line1_Label2|Line1_Label3",
#"Line2_Label1|Line2_Label2|Line2_Label3",
#"Line3_Label1|Line3_Label2|Line3_Label3",
nil];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [myData count];
}
// Return a cell for the table
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// A cell identifier which matches our identifier in IB
static NSString *CellIdentifier = #"CellIdentifier";
// Create or reuse a cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Get the cell label using its tag and set it
NSString *currentItem = [myData objectAtIndex:indexPath.row];
NSArray *itemArray = [currentItem componentsSeparatedByString:#"|"];
UILabel *cellLabel = (UILabel *)[cell viewWithTag:1];
[cellLabel setText:itemArray[0]];
UILabel *cellLabel2 = (UILabel *)[cell viewWithTag:3];
[cellLabel2 setText:itemArray[1]];
UILabel *cellLabel3 = (UILabel *)[cell viewWithTag:4];
[cellLabel3 setText:itemArray[2]];
// get the cell imageview using its tag and set it
UIImageView *cellImage = (UIImageView *)[cell viewWithTag:2];
[cellImage setImage:[UIImage imageNamed: #"control.png"]];
return cell;
}
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"ShowSelectedMovie"]) {
// Get reference to the destination view controller
ItemViewController *vc = [segue destinationViewController];
// get the selected index
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
// Pass the name and index of our film
[vc setSelectedItem:[NSString stringWithFormat:#"%#", [myData objectAtIndex:selectedIndex]]];
[vc setSelectedIndex:selectedIndex];
}
}
#end

First of all, make an IBOutlet of the tableview in the .h and synthesize it in the .m.
Then make an action to the edit button (if you don't already have one). In the action, write:
CGRect rect = yourTableView.cell.contentView.frame;
//Do whatever changes you wish to do with the sizing of the view. origin changes placement and size changes size (duh). Line below is an example.
rect.origin.y = yourTableView.cell.contentView.frame.origin.y - 20;
yourTableView.cell.contentView.frame = rect;
This won't be animated, but I think it'll fulfill your purpose.

Overwrite the -(void)layoutSubviews{} - method of your custom UITableViewCellController.m or if you don't use a custom UITableViewCellController, try it in your UITableViewController. But I haven't tried it yet with no custom UITableViewCellController.
Something like this will do the trick:
-(void) layoutSubviews {
[super layoutSubviews];
CGFloat xPositionOfElementInTableCell = 273.0f; /* the position of the element before going into edit mode */
if (self.isEditing && !self.showingDeleteConfirmation) // if we enter editing mode but not tapped on the red minus at the moment
{
xPositionOfElementInTableCell = 241.0f;
} else if (self.isEditing && self.showingDeleteConfirmation) // after we tappet on the red minus
xPositionOfElement = 193.0f;
}
CGRect frameOfElementInTableCell = self.myElementInTableCell.frame;
frameOfElementInTableCell.origin.x = xPositionofElement;
self.myElementInTableCell.frame = frameOfElementInTableCell;
}
I hope it helps you. The idea for this code is not mine. I found it here in SO, too. Don't know where exactly.

Related

NSTableViewCell selectedRow number for IBAction click

I'm running into a simple problem but have yet to find an optimal solution. I have a view based NSTableView that is loading it's cell views from different xibs. My table view is dynamic and based on user input I will dynamically add and remove rows ultimately adjusting the table data source. Each one of my NSTableCellViews have a button in it and I link the IBAction click handler to the NSView that holds the table view. What I need to do is get the row number for the button that was clicked in the table view so I can process the logic. I am able to do this successfully in : tableViewSelectionDidChange:(NSNotification *)notification
Here is how I do it:
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
NSTableView *tableView = [notification object];
NSInteger selectedRow = [tableView selectedRow];
}
This works perfectly for a user actually clicking the row. Now when I move the NSButton IBAction and link it in the NSView as follows:
- (IBAction)buttonClickHandler:(NSButton *)sender {
NSInteger selectedRow = [self.tblView rowForView:sender];
NSLog(#"%ld", (long)selectedRow);
}
I based this approach from this selected answer.
I also tried this:
- (IBAction)buttonClickHandler:(NSButton *)sender {
id representedObject = [(NSTableCellView *)[sender superview] objectValue];
NSLog(#"%#", representedObject);
}
//My configuration
- (void)configureView {
[self.view setFrame:[self bounds]];
[self addSubview:self.view];
[self.view setWantsLayer:YES];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.tblView.delegate = self;
self.tblView.dataSource = self;
[self.tblView setIntercellSpacing:NSMakeSize(0, 0)];
[self.tblView registerNib: [[NSNib alloc] initWithNibNamed:#"ParentCellXib" bundle:nil] forIdentifier:#"ParentCell"];
[self.tblView registerNib: [[NSNib alloc] initWithNibNamed:#"ChildCellXib" bundle:nil] forIdentifier:#"ChildCell"];
[self.tblView registerNib: [[NSNib alloc] initWithNibNamed:#"HeaderCellXib" bundle:nil] forIdentifier:#"HeaderCell"];
}
But the represented object returns null. If it's worth mentioning, I've set my File's Owner as the View that holds the tableView so I can link the IBAction and I've subclassed the TableCellView to a different class. However, I don't think this is part of the problem as far as I can see. Is there a simple solution to reliably give me the selectedRow number based on a button click in that cell? Both approaches I tried above return -1 and null respectively.
I would set the row in NSButton's tag property:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
SomeTableCellView *cell = [tableView makeViewWithIdentifier:#"cell" owner:self];
if (cell == nil) {
cell = // init some table cell view
cell.identifier = #"cell";
}
cell.button.tag = row;
[cell.button setTarget:self];
[cell.button setAction:#selector(buttonAction:)];
}
- (IBAction)buttonAction:(id)sender {
NSLog(#"row: %d", sender.tag);
}
Try This
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
yourCustomeCell *aCell;
NSString *aStrIdentifier = #"yourIdentiFier";
aCell = (yourCustomeCell *)[tableView dequeueReusableCellWithIdentifier:aStrIdentifier];
//you have to set your indexpath
objc_setAssociatedObject(aCell.btnUpload_or_Add, #"objBtn", indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[aCell.YourButton addTarget:self action:#selector(yourButtonActiontapped:) forControlEvents:UIControlEventTouchUpInside];
return aCell;
}
-(IBAction)yourButtonActiontapped:(UIButton *)sender{
NSIndexPath *aIndPath = objc_getAssociatedObject(sender, #"objBtn");
NSLog(#"row:%#",aIndPath.row);
}
also you have to import #import <objc/runtime.h>
another way to get row in IBAction is TAG but objc is better option insted of TAG.
Create a subclass of UIButton and add a property for NSIndexPath for the button. Use this button in cellForRowAtIndexPath method. assign the index path of the cell to that of index path of the button.
On Tap, get the index path from its sender. In your case index path of that button.

Uislider in a tableView Cell

hi everyone i'm new in iOS programming ! I have a custom table view controller with custom table view cell ! one of those cell have a uislider and a label ! I want to change label text when slider change value ! this is my code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *cellInfo = [[self.sections objectAtIndex:currentTab] objectAtIndex:indexPath.row];
HLNotificheCell *cell = [tableView dequeueReusableCellWithIdentifier:[cellInfo objectForKey:#"cell"] forIndexPath:indexPath];
UIImageView *radioIndicator = (UIImageView *)[cell.contentView viewWithTag:200];
radioIndicator.image = (currentBullet != indexPath.row) ? [UIImage imageNamed:#"RadioOff"] : [UIImage imageNamed:#"RadioOn"];
UIImageView *av = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 80)];
av.backgroundColor = [UIColor clearColor];
av.opaque = NO;
av.image = [UIImage imageNamed:#"NewsSeparetor.png"];
cell.backgroundView = av;
cell.slider.maximumValue = 100;
cell.slider.minimumValue = 1;
cell.slider.continuous = TRUE;
//set a method which will get called when a slider in a cell changes value
[cell.slider addTarget:self action:#selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
//Keep a reference to each slider by assigning a tag so that we can determine
//which slider is being changed
cell.slider.tag = indexPath.row;
//Grab the value from the sliderValuesArray and set the slider knob to that position
return cell;
}
-(void)sliderChanged:(UISlider*)sender{
HLNotificheCell *cell = [[HLNotificheCell alloc]init];
if (sender == cell.slider) {
cell.label.text = [NSString stringWithFormat:#"%0.3f", cell.slider.value];
}
}
Actually there is a lot of bad practices in your code. Please let me explain.
Let's begin with your HLNotificheCell class. I think header file should look like this:
#import <UIKit/UIKit.h>
#define HLNotificheCellIdentifier #"HLNotificheCellIdentifier"
#interface HLNotificheCell : UITableViewCell
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
#property (strong, nonatomic) UISlider *slider;
#property (strong, nonatomic) UIImageView *radioIndicator;
#end
and implementation file:
#import "HLNotificheCell.h"
#implementation HLNotificheCell
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (self) {
_slider = [[UISlider alloc] init];
_slider.maximumValue = 100;
_slider.minimumValue = 1;
_slider.continuous = YES; //YES is more natural in objc rather than TRUE.
[self addSubview: _slider];
_radioIndicator = [[UIImageView alloc] init];
[self addSubview:_radioIndicator];
UIImageView *av = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 80)];
av.backgroundColor = [UIColor clearColor];
av.opaque = NO;
av.image = [UIImage imageNamed:#"NewsSeparetor.png"];
self.backgroundView = av;
//it's better to use built-in textLabel instead of creating your own. Trust me when you will have 20 different customized cells you will get lost with their names.
self.textLabel.textColor = [UIColor redColor];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
// layout your self.slider and self.radioIndicator here or use xib for it.
// e.g. this will layout slider to fit whole cell:
self.slider.frame = self.bounds;
}
#end
Ok, lets go now to cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// try to dequeue cell if exist
HLNotificheCell *cell = (HLNotificheCell *)[tableView dequeueReusableCellWithIdentifier:HLNotificheCellIdentifier];
// if doesn't, create new one.
if (!cell) { // is enough to set slider target only once when cell is created. When reuse is not needed.
cell = [[HLNotificheCell alloc] initWithReuseIdentifier:HLNotificheCellIdentifier];
[cell.slider addTarget:self action:#selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
}
//set image as you wish:
cell.radioIndicator.image = (currentBullet != indexPath.row) ? [UIImage imageNamed:#"RadioOff"] : [UIImage imageNamed:#"RadioOn"];
//Keep a reference to each slider by assigning a tag so that we can determine
//which slider is being changed
cell.slider.tag = indexPath.row;
//Grab the value from the sliderValuesArray and set the slider knob to that position
NSNumber *sliderValue = sliderValuesArray[indexPath.row];
[cell.slider setValue:sliderValue.floatValue animated:NO]
return cell;
}
and sliderChanged: method:
-(void)sliderChanged:(UISlider*)sender{
// You cannot do this:
// HLNotificheCell *cell = [[HLNotificheCell alloc]init];
// because you have to restore reference from sender.tag as you wrote in cellForRowAtIndexPath method:
HLNotificheCell *cell = (HLNotificheCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:slider.tag inSection:0]] // I assume you have only 1 section
cell.textLabel.text = [NSString stringWithFormat:#"%0.3f", cell.slider.value];
//save new value to the sliderValuesArray
self.sliderValuesArray[indexPath.row] = #(cell.slider.value);
}
Assumptions:
when you will use this part of code please do not use registerClass:forCellReuseIdentifier:
yours sliderValuesArray is kind of NSMutableArray class.
sliderValuesArray has been initialized with size same as number of cells, like:
self.sliderValuesArray = [[NSMutableArray alloc] initWithCapacity:<#numberOfCels#>];
for (int i = 0; i < sliderValuesArray.count; i++) {
sliderValuesArray[i] = #(0);
}
your table view contains only one type of cells (HLNotificheCell)
There could be some typos and/or lack of semicolons because I wrote it without compiler.
I doing this easier. Apple write that you can use IBActions for static rows. (You can read about it here in The Technique for Static Row Content. But I already tested it on iOS 9 with dynamic cells and it's just works :)
At first - Custom cell with IBAction
#interface SliderTableViewCell ()
#property (weak, nonatomic) IBOutlet UILabel *sliderValueLabel;
#property (weak, nonatomic) IBOutlet UISlider *slider;
#end
#implementation SliderTableViewCell
- (void)awakeFromNib {
self.slider.minimumValue = 1;
self.slider.maximumValue = 1000;
}
- (IBAction)sliderValueChanged:(id)sender {
self.sliderValueLabel.text = [NSString stringWithFormat:#"1_%.f", self.slider.value];
}
#end
Second - TableView Delegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kReuseIdentifierSliderCell];
cell.slider.value = 142;
cell.sliderValueLabel.text = #"1_142";
return cell;
}
Third - Run your app and enjoy yourself ;)

UITableView swap cells

In my UITable view, i want to be able to switch the positions of two cells when you drag one on to the other. I have already completed this task. The only problem is that when you try to drag one over the other, all the rows that follow shift down to make room for the new one. I do not want this, I want the cells to essentially stay visually static when you are dragging the first cell around. Any ideas?
Thanks,
Mitch
I think you can do this with some trickery, which is after all, how Apple does a lot of its animations.
Start with a UIViewController with a table view as a subview
Add a pan gesture recognizer to the main view (you'll have to implement shouldRecognizeSimultaneouslyWithGestureRecognizer: and return YES, so it will work with the table view's own gesture recognizers)
When you start to drag, create a snapshot view of the cell you started the drag over, and add it as a subview of the main view. You'll probably want to disable the table view's scrolling at this point also.
Drag that snapshot view using the pan gesture recognizer's translationInView property
When you drop the view, delete the snapshot view, and update the table's data source to show the data in the new order you created with the move.
I haven't tried all of this yet (but I've used some elements of it in other projects), but I think this should give you a start on what you're trying to achieve. There are some more details to work out for sure -- what do you want to see in the spot from where you dragged the cell? A blank space? What do you want to see when the dragged view is dropped?
After Edit:
This is what I have so far, and it works pretty well. In the storyboard, I have a UITableViewController with two cell prototypes, both basic types. The one whose identifier is "Blank" just has no text in its label. The tableviewController is embedded in a navigation controller, and I've added a button to the navigation bar with the initial title of "Drag" -- this button is connected to the toggleDragging method.
#interface TableController ()
#property (strong,nonatomic) NSMutableArray *theData;
#property (strong,nonatomic) UIPanGestureRecognizer *panner;
#property (strong,nonatomic) UIView *cellSnapshotView;
#property (strong,nonatomic) NSIndexPath *draggingCellIndexPath;
#end
#implementation TableController
- (void)viewDidLoad {
[super viewDidLoad];
self.panner = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCellImage:)];
[self.tableView addGestureRecognizer:self.panner];
self.panner.enabled = NO;
self.panner.delegate = self;
self.draggingCellIndexPath = [NSIndexPath indexPathForRow:-1 inSection:0];
self.theData = [#[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Black",#"Brown",#"Red",#"Orange",#"Yellow",#"Green",#"Blue",#"Violet",#"Gray",#"White"] mutableCopy];
}
-(IBAction)toggleDragging:(UIBarButtonItem *)sender {
if ([sender.title isEqualToString:#"Drag"]) {
self.panner.enabled = YES;
sender.title = #"Scroll";
}else{
self.panner.enabled = NO;
sender.title = #"Drag";
self.tableView.scrollEnabled = YES;
}
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(IBAction)moveCellImage:(UIPanGestureRecognizer *)panner {
if (! self.cellSnapshotView) {
CGPoint loc = [panner locationInView:self.tableView];
self.draggingCellIndexPath = [self.tableView indexPathForRowAtPoint:loc];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:self.draggingCellIndexPath];
self.cellSnapshotView = [cell snapshotViewAfterScreenUpdates:YES];
self.cellSnapshotView.alpha = 0.8;
self.cellSnapshotView.layer.borderColor = [UIColor redColor].CGColor;
self.cellSnapshotView.layer.borderWidth = 1;
self.cellSnapshotView.frame = cell.frame;
[self.tableView addSubview:self.cellSnapshotView];
self.tableView.scrollEnabled = NO;
[self.tableView reloadRowsAtIndexPaths:#[self.draggingCellIndexPath] withRowAnimation:UITableViewRowAnimationNone]; // replace the cell with a blank one until the drag is over
}
CGPoint translation = [panner translationInView:self.view];
CGPoint cvCenter = self.cellSnapshotView.center;
cvCenter.x += translation.x;
cvCenter.y += translation.y;
self.cellSnapshotView.center = cvCenter;
[panner setTranslation:CGPointZero inView:self.view];
if (panner.state == UIGestureRecognizerStateEnded) {
UITableViewCell *droppedOnCell;
CGRect largestRect = CGRectZero;
for (UITableViewCell *cell in self.tableView.visibleCells) {
CGRect intersection = CGRectIntersection(cell.frame, self.cellSnapshotView.frame);
if (intersection.size.width * intersection.size.height >= largestRect.size.width * largestRect.size.height) {
largestRect = intersection;
droppedOnCell = cell;
}
}
NSIndexPath *droppedOnCellIndexPath = [self.tableView indexPathForCell:droppedOnCell];
[UIView animateWithDuration:.2 animations:^{
self.cellSnapshotView.center = droppedOnCell.center;
} completion:^(BOOL finished) {
[self.cellSnapshotView removeFromSuperview];
self.cellSnapshotView = nil;
NSIndexPath *savedDraggingCellIndexPath = self.draggingCellIndexPath;
if (![self.draggingCellIndexPath isEqual:droppedOnCellIndexPath]) {
self.draggingCellIndexPath = [NSIndexPath indexPathForRow:-1 inSection:0];
[self.theData exchangeObjectAtIndex:savedDraggingCellIndexPath.row withObjectAtIndex:droppedOnCellIndexPath.row];
[self.tableView reloadRowsAtIndexPaths:#[savedDraggingCellIndexPath, droppedOnCellIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}else{
self.draggingCellIndexPath = [NSIndexPath indexPathForRow:-1 inSection:0];
[self.tableView reloadRowsAtIndexPaths:#[savedDraggingCellIndexPath] withRowAnimation:UITableViewRowAnimationNone];
}
}];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.theData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self.draggingCellIndexPath isEqual:indexPath]) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Blank" forIndexPath:indexPath];
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.theData[indexPath.row];
return cell;
}
This may be helpful, you can respond however you like to the delegate calls to achieve this effect:
https://github.com/alfiehanssen/AMHEditableTableView
Let me know if you have any questions,

Display centered 200x150 Image in a UITableViewCell

I need to display a centered 200x150 image in a UITableViewCell. I was able to add the image to a standard cell type (it shrunk it to fit - that wasn't working). I then tried redrawing it by setting the bounds and frame for the image (this caused overlap between my image and the other rows).
I have a custom class inheriting from UITableViewCell:
#import "WCPictureViewCell.h"
#implementation WCPictureViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.frame = CGRectMake(5,5,210,160);
self.bounds = CGRectMake(5,5,210,160);
self.imageView.frame = CGRectMake(0,0,200,150);
self.imageView.bounds = CGRectMake(0,0,200,150);
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
This code produces picture overlap with the rest of my table:
Here is my tableView:cellForRowAtIndexPath: method in my controller:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if(cellPropertyMap>0) {
static NSString *CellIdentifier = #"DetailCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
[[cell textLabel] setText: (NSString *)[[self displayLabels]objectAtIndex:cellPropertyMap]];
[[cell detailTextLabel] setText: (NSString *)[[self displayData]objectAtIndex:cellPropertyMap++]];
} else {
static NSString *CellIdentifier = #"PictureCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//if(cell == nil) {
//}
// Configure the cell...
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: [cat friendlyURLPath]]];
cell.imageView.image = [UIImage imageWithData: imageData];
cellPropertyMap++;
return cell;
}
return cell;
}
How do I force my table cells to respect eachother's sizes and not overlap? How do I get my image to center?
You need to use
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath)
{
// add logic to determine if this indexPath is an image...
if (indexPath.row == 0) // first row in a section is an image?
return (150.0f); // this row is an image
else
return (44.0f); // this is a standard data row
}
to return the correct height of your TableView row.
Second step is to get the image to center, there are various techniques. Try
UIImageView *yourImage = ...;
// set frame and auto-resizing mask
yourImage.frame = CGRectMake(tableView.bounds.size.width / 2) - (yourImage.size.width / 2), 0, yourImage.size.width, yourImage.size.height);
yourImage.autoResizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
// add UIImage to the cell contentView
[cell.contentView addSubview:yourImage];
don't set the cell.imageView property, which defines a left justified image in the cell.
Lastly, and off-topic from your question, you should really consider lazy-loading the images, using initWithContentsOfURL on the UI thread will not yield a good user experience.

Sizing a Custom UITableViewCell and included UIImageView based on Image Size

I am trying to customize the UITableViewCell below for an iPhone app in a grouped table view. What I would like to do is have the image width take up the whole cell minus padding (280) and the height variable based on the image size.
Currently I am using SDWebImage to asynchronously download remote images. This may not be the correct thing to do in this case. I am also having trouble figuring out how to give the custom cell the image on initialization. The image URL is stored in self.beerPhoto in the DetailViewController.
I have searched for this a number of ways and have not found exactly what I am looking for. The closest was this: How to scale a UIImageView proportionally, but this method seems to require the cell to have the image at initialization, as I tried to make this code work but setting the image after initialization left a blank cell.
The current code includes constants I set to approximate an image in portrait orientation. In reality some of the images are portrait and some are landscape orientation.
Please let me know if there's anything additional you need to know.
Header for custom UITableViewCell:
#import <UIKit/UIKit.h>
#interface BeerDetailHead : UITableViewCell {
UILabel *beerName;
UIImageView *beerImage;
}
#property(nonatomic, retain)UILabel *beerName;
#property(nonatomic, retain)UIImageView *beerImage;
#end
Relevant portion of implementation for custom UITableViewCell
#import "BeerDetailHead.h"
#implementation BeerDetailHead
#synthesize beerName, beerImage;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
//beerName = [[UILabel alloc]init];
//beerName.textAlignment = UITextAlignmentLeft;
//beerName.font = [UIFont systemFontOfSize:14];
beerImage = [[UIImageView alloc]init];
//[self.contentView addSubview:beerName];
[self.contentView addSubview:beerImage];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGRect contentRect = self.contentView.bounds;
CGFloat boundsX = contentRect.origin.x;
CGRect frame;
frame= CGRectMake(boundsX+10 ,10, 280, 375);
beerImage.frame = frame;
}
DetailViewController's cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
NSArray *listData =[self.tableContents objectForKey:
[self.sortedKeys objectAtIndex:[indexPath section]]];
NSLog(#"listData = %#", listData);
NSUInteger row = [indexPath row];
if ([self.sortedKeys objectAtIndex:[indexPath section]] == #"header"){
static NSString *headerTableIdentifier = #"HeaderTableIdentifier";
BeerDetailHead * headerCell = (BeerDetailHead*)[tableView
dequeueReusableCellWithIdentifier: headerTableIdentifier];
if(headerCell == nil) {
headerCell = [[[BeerDetailHead alloc] initWithFrame:CGRectZero reuseIdentifier:headerTableIdentifier] autorelease];
}
headerCell.beerName.text = [listData objectAtIndex:row];
[headerCell.beerImage setImageWithURL:[NSURL URLWithString:self.beerPhoto]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
//NSLog(#"frame = %#", headerCell.beerImage.frame);
return headerCell;
}
else{
//use standard UITableViewCell
}
}
Implement tableView:heightForRowAtIndexPath: delegate method and return calculated height for each row from this method.
After loading each image call reloadData on your tableView OR if you want to animate changes call:
[self.tableView beginUpdates];
[self.tableView endUpdates];
Also, you might want to combine several sequence height updates into one. I would use I little delay to perform this:
// triggerUpdates calls the above code
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(triggerUpdates) object:nil];
[self performSelector:#selector(triggerUpdates) withObject:nil afterDelay:0.1];