THE CODE:
ViewController.m
#interface ViewController ()
#property (strong,nonatomic) NSMutableArray<NSString *> *sections;
#property (strong,nonatomic) NSMutableArray<NSMutableArray<TableItem *> *> *items;
#property (strong,nonatomic) NSMutableArray<TableItem *> *sectionItems;
#end
...
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView {
return _items.count;
}
- (NSString *) tableView: (UITableView *) tableView titleForHeaderInSection:(NSInteger) section {
NSLog(_sections[section]);
return _sections[section];
}
-(NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection:(NSInteger)section {
return _items[section].count;
}
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath ];
TableItem *item = _items[indexPath.section][indexPath.row];
cell.textLabel.text = item.title;
cell.detailTextLabel.text = item.theDescription;
return cell;
}
All appears as it should. When I scroll down and back up, the description labels have disappeared.
Some context:
TableItem.h
#interface TableItem : NSObject
#property (weak,nonatomic) NSString *title;
#property (weak,nonatomic) NSString *theDescription;
-(instancetype) initWithTitle: (NSString *) title theDescription: (NSString *) theDescription;
#end
It seems to be a problem with dequeueReusableCell. I know this has been asked before, but I checked all I could find and found no answer to my issue.
Possibly, the problem lies in TableItem properties. They are weak, which means actual strings can be freed and nilled at any moment.
So, by the time you scroll up and down, title and theDescription are already nils.
Change your TableItem class:
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *theDescription;
Or, if you want to be sure that after assigning new value to its properties, these values can't be modified, use copy:
#property (copy, nonatomic) NSString *title;
#property (copy, nonatomic) NSString *theDescription;
Related
I have a model class with 3 properties and tableview with three cells and each one has its own custom class.
How to install and fill three different cells from the same model correctly?
#interface Movie : NSObject
#property (nonatomic, strong) UIImage *posterImage;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *overview;
#end
#interface MoviePosterCell : UITableViewCell
#property (nonatomic, strong) UIImageView *poster;
#end
#interface MovieTitleCell : UITableViewCell
#property (nonatomic, strong) UILabel *title;
#end
#interface MovieOverviewCell : UITableViewCell
#property (nonatomic, strong) UILabel *overview;
#end
I tried to do something like this, but it seems to me wrong because if I want to reorder or add a new cell I will have to install everything manually.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0)
{
MovieOverviewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MovieOverviewCell class]) forIndexPath:indexPath];
cell.poster = self.movie.posterImage;
return cell;
}
if (indexPath.row == 1)
{
MovieTitleCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MovieTitleCell class]) forIndexPath:indexPath];
cell.title = self.movie.title;
return cell;
}
if (indexPath.row == 2)
{
MovieOverviewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MovieOverviewCell class]) forIndexPath:indexPath];
cell.overview = movie.overview;
return cell;
}
return [UITableViewCell new];
}
I created a class with some properties. In the storyboard I have a tableview controller. I want to pass data from the class to the tableview. In each cell I have some labels. I want to fill the data but I haven't success with the arrays. Can you please help me.
So I have a City class with some properties for ex. property1, property2, which are labels
#import <Foundation/Foundation.h>
#interface DACityObject : NSObject {
NSNumber *ID;
NSString *name;
NSNumber *temperature;
NSNumber *minimumTemperature;
NSNumber *maximumTemperature;
NSString *iconName;
}
#property (strong,nonatomic) NSNumber *ID;
#property (strong,nonatomic) NSString *name;
#property (strong,nonatomic) NSNumber *temperature;
#property (strong,nonatomic) NSNumber *minimumTemperature;
#property (strong,nonatomic) NSNumber *maximumTemperature;
#property (strong,nonatomic) NSString *iconName;
#end
and .m file
#import "DACityObject.h"
#implementation DACityObject
#synthesize ID,name,temperature,minimumTemperature,maximumTemperature,iconName;
#end
DACityCell.h
#import <UIKit/UIKit.h>
#interface DACityCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *name;
#property (strong, nonatomic) IBOutlet UILabel *temperature;
#property (strong, nonatomic) IBOutlet UILabel *minimumTemperature;
#property (strong, nonatomic) IBOutlet UILabel *maximumTemperature;
#property (strong, nonatomic) IBOutlet UIImageView *iconName;
#end
DACityCell.m
#import "DACityCell.h"
#implementation DACityCell
#synthesize name,temperature,minimumTemperature,maximumTemperature,iconName;
- (void)awakeFromNib {
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = CGRectMake(0,45,75,75);
}
#end
How can I create a data for example 3 cities and put it in the table view in my MainController?
Let's say you've already created your 3 data via the class object DACityObject, all you have to do now is to place them in an array say dataArray (NSMutableArray) and call them in the method cellForRowAtIndexPath in your class that has the UITableView, then just call your custom tableview cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellIdentifier";
// extract data here
DACityObject *dataObject = [dataArray objectAtIndex:indexPath.row];
DACityCell *cell = (DACityCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DACityCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.name.text = dataObject.name;
cell.temperature.text = dataObject.temperature;
.
.
.... etc.
return cell;
}
i have a UITableView in a storyboard in an ios xcode project,
i also have an array of menuitem objects stored in itemarray:
NSMutableArray *itemarray;
this is my menuitem.h file
#import <Foundation/Foundation.h>
#interface menuitem : NSObject
#property (nonatomic) NSString *itemtitle;
#property (nonatomic) NSString *itemdesc;
#property (nonatomic) int itemid;
#property (nonatomic) int itemprice;
#property (nonatomic) NSString *itemcat;
#end
this is my tableviewcell.m file:
#import "tableviewcell.h"
#import "menuitem.h"
#interface tableviewcell ()
#property (nonatomic, weak) IBOutlet UILabel *titleLabel;
#property (nonatomic, weak) IBOutlet UILabel *descLabel;
#property (nonatomic, weak) IBOutlet UILabel *priceLabel;
#end
#implementation tableviewcell
-(void)configureWithmenuitem:(menuitem *)item{
NSLog(#"hello");
self.titleLabel.text = item.itemtitle;
self.descLabel.text = item.itemdesc;
self.priceLabel.text = [NSString stringWithFormat:#"%.1d", item.itemprice];
}
#end
i want to get the itemtitle, itemdesc and itemprice from each instance of menuitem into 3 labels on each of the tableviewcells
Your tableviewcell is a View and thus it should only know how to display itself and not what to display. It's the job of your controller (through the delegate tableView:cellForRowAtIndexPath: method) to tell the view what to display.
Thus you should do something like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Id"];
//Assuming you've stored your items in an array.
Item *item = [items objectAtIndex:indexPath.row];
cell.titleLabel.text = item.itemTitle;
cell.descLabel.text = item.itemdesc;
cell.priceLabel.text = [NSString stringWithFormat:#"%.1d", item.itemprice];
return cell;
}
^ The above code assumes you have a prototype cell. If you don't, you'll have to alloc if cell is nil after dequeue.
I have an iOS app that makes a request to a web-service which returns JSON formatted data. There is a predefined class in my iOS app that inherits and implements the JSONModel Framework, to which this returned data is bound to as an NSMutableArray containing these objects. The TableView's data is generated from these objects.
My conundrum is that in my custom UITableViewCell I allow the user to change some of the data presented, and I need the ability to save that back to the classes which can be serialized and sent via POST back to the web-service.
Custom Cell .h:
#interface EnclosureDetailCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *enclosureNumber;
#property (weak, nonatomic) IBOutlet UITextField *QTY;
#property (weak, nonatomic) IBOutlet UIStepper *stepper;
#property (weak, nonatomic) IBOutlet DeSelectableSegmentControl *enclosureStatus;
- (IBAction)valueChanged:(UIStepper *)sender;
- (IBAction)changedTextValue:(id)sender;
#end
Custom Cell .m:
#implementation EnclosureDetailCell
- (IBAction)changedTextValue:(id)sender
{
self.stepper.value = self.QTY.text.intValue;
}
- (IBAction)valueChanged:(UIStepper *)sender
{
int stepperValue = sender.value;
self.QTY.text = [NSString stringWithFormat:#"%i", stepperValue];
}
- (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
Model Class (.h):
#protocol Enclosure #end
#interface Enclosure : JSONModel
#property (nonatomic, strong) NSString *EnclosureNumber;
#property (nonatomic, strong) NSString *InventoryID;
#property (nonatomic, strong) NSString *UseInventoryID;
#property (nonatomic) int CensusQTY;
#property (nonatomic) BOOL Verified;
#property (nonatomic) BOOL MissingEnclosure;
#property (nonatomic) BOOL RetireEnclosure;
#end
TableViewController (partial)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
EnclosureDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
ProtocolEnclosure *loc = (ProtocolEnclosure *)_objects[indexPath.section];
Enclosure *enc = (Enclosure *) loc.Enclosures[indexPath.row];
cell.enclosureNumber.text = enc.EnclosureNumber;
cell.QTY.text =[NSString stringWithFormat:#"%i", enc.CensusQTY];
cell.stepper.value = enc.CensusQTY;
if (enc.Verified)
{
cell.QTY.enabled = false;
cell.stepper.enabled = false;
cell.enclosureStatus.selectedSegmentIndex = Verified;
}
else if (enc.MissingEnclosure)
cell.enclosureStatus.selectedSegmentIndex = MissingEnclosure;
else if (enc.RetireEnclosure)
cell.enclosureStatus.selectedSegmentIndex = RetireEnclosure;
else
cell.enclosureStatus.selectedSegmentIndex = None;
return cell;
}
enum{
Verified = 0,
MissingEnclosure = 1,
RetireEnclosure = 2,
None = -1
};
So in my UITableViewCell I have a text field that corresponds to CensusQTY and a SegmentControl who's selection corresponds to Verified/MissingEnclosure/RetireEnclosure.
How can I go about saving the data the user has changed via the UI back into the model class?
I obviously can't iterate over each of the UITableView rows - because of dequeue, I will only get the ones that are currently on screen.
Any thoughts on how this could be accomplished?
Thanks!
There are probably many ways to do that, the cleanest way that comes to mind would be to create a delegate for your custom cell. (That you could declare in .h). Your cell should add a index property to keep track of what instance of Enclosure it's referring to.
#class EnclosureDetailCell;
#protocol EnclosureCellDelegate
#required
- (void) qtyDidUpdate:(EnclosureDetailCell*)cell;
- (void) stepperDidUpdate:(EnclosureDetailCell*)cell;
#end
#interface EnclosureDetailCell : UITableViewCell
#property (nonatomic,assign) NSInteger index;
#property (nonatomic,weak) id<EnclosureCellDelegate> delegate;
....
In your .m you would have to call your delegate
#implementation EnclosureDetailCell
- (IBAction)changedTextValue:(id)sender
{
self.stepper.value = self.QTY.text.intValue;
[self.delegate qtyDidUpdate:self];
}
- (IBAction)valueChanged:(UIStepper *)sender
{
int stepperValue = sender.value;
self.QTY.text = [NSString stringWithFormat:#"%i", stepperValue];
[self.delegate stepperDidUpdate:self];
}
You'd have to implement those methods in your TableViewController. It would look like :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
EnclosureDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.index = indexPath.row;
cell.delegate = self;
ProtocolEnclosure *loc = (ProtocolEnclosure *)_objects[indexPath.section];
Enclosure *enc = (Enclosure *) loc.Enclosures[indexPath.row];
....
}
- (void) qtyDidUpdate:(EnclosureDetailCell*)cell{
Enclosure *enc = (Enclosure *)loc.Enclosures[cell.index];
//Here you can update directly the items of your array
}
- (void) stepperDidUpdate:(EnclosureDetailCell*)cell{
Enclosure *enc = (Enclosure *)loc.Enclosures[cell.index];
//Here you can update directly the items of your array
}
That way you will be able to keep your whole array updated, and be able to send your new data to your web service whenever you like.
Probably the easiest way would be to have the cell own a weak reference to the Model object which you update in the IBAction methods of the cell.
#interface EnclosureDetailCell : UITableViewCell
...
#property(nonatomic, weak) Enclosure *enclosure;
...
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
Enclosure *enc = (Enclosure *) loc.Enclosures[indexPath.row];
cell.enclosure = enc;
...
}
#implementation EnclosureDetailCell
- (IBAction)changedTextValue:(id)sender
{
self.stepper.value = self.QTY.text.intValue;
//you have access to self.enclosure, do what you want
}
- (IBAction)valueChanged:(UIStepper *)sender
{
int stepperValue = sender.value;
self.QTY.text = [NSString stringWithFormat:#"%i", stepperValue];
//you have access to self.enclosure, do what you want
}
I have created a UITableViewCell and I am trying to display it in one of my table, but for some reason it doesn't come, the table is coming empty. And when I click on the one of cell in the table it takes me down to next level, which means the custom UITableViewCell is not getting rendered properly.
Here is my custom UITableViewCell .h file
#interface ProductViewCell : UITableViewCell {
IBOutlet UILabel *titleLabel;
IBOutlet UILabel *detailLabel;
IBOutlet UIImageView *imageView;
}
#property (nonatomic, retain) UILabel *titleLabel;
#property (nonatomic, retain) UILabel *detailLabel;
#property (nonatomic, retain) UIImageView *imageView;
Here is the .m file
#import "ProductViewCell.h"
#implementation ProductViewCell
#synthesize titleLabel, detailLabel, imageView;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
#end
Here is the .h file which subclasses UITableViewController
#interface ProductCategoryViewController : UITableViewController {
NSArray *tableDataSource;
}
#property (nonatomic, retain) NSArray *tableDataSource;
Here is the .m file for ProductCategoryViewController
#implementation ProductCategoryViewController
#synthesize tableDataSource;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
ProductViewCell *cell = (ProductViewCell*)[tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[ProductViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
cell.titleLabel.text = [dictionary objectForKey:#"name"];
cell.imageView.image = [UIImage imageNamed:[dictionary objectForKey:#"imageUrl"]];
return cell;
}
Your help is appreciated.
Thanks,
Yogesh
I got this working by making some changes, in my tableviewcontroller I added an IBOutlet for ProductViewCell and synthesis the same.
Then I changed the xib file for ProductViewCell
1) I make File's Owner to my tableViewController
2) Then here I linked the the IBOutlet that I created in the tableViewContoller to the ProductViewCell.
I am not sure if this is the right way to do it but atleast this is working for me now