NSScrollView displaying NSTableView based on custom view (Image, checkbox and text label). When I scroll - I have lag (bug?) with redrawing rows.
Normal:
Bugged after scroll:
Project (and .xib file) was updated from Xcode 4 to Xcode 5 format. I think this bug appeared after it, but I'm not sure.
Any suggestions how to fix it?
Realisation of NSTableViewDataSource, NSTableViewDelegate protocols:
- (NSInteger) numberOfRowsInTableView: (NSTableView *) aTableView {
return [arrayOfObjects count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
CMListItem *currentObject = [arrayOfObjects objectAtIndex: row];
NSString *currentName = [currentObject name];
BOOL currentState = [currentObject state];
NSImage *currentImage = [currentObject getArtwork];
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"MainCell"]) {
CMTableCellView *cellView = [tableView makeViewWithIdentifier: identifier owner: self];
cellView.textField.stringValue = currentName;
cellView.button.state = currentState;
cellView.imageView.image = currentImage;
return cellView;
}
return nil;
}
NSScrollView not modified.
My own table cell view - subclass of NSTableCellView with few additional outlets.
UPDATE: this thing helped me: set Can Draw Concurrently for all NSTableView at On state, then turn it to Off state.
I think I have seen this problem sometimes when "Copy On Scroll" is enabled. Try disabling this option in IB (in the attribute inspector, scrollview section, behavior).
Related
My NSCollectionView is drawing my NSCollection items over each other.
UPDATE: I have added a sample project
GitHub Sample Project
UPDATE: This has changed somewhat
When the app first launches it looks like this
UPDATE
My current example has two views which are currently in there own nib files, with dedicated NScollectionViewItem objects they are currently the same for testing. I basically have a NSCollectionViewItem which has as it's child a view with the NSTextField in it. With all of the constraints.
For the Collection View it is setup as a Grid Controller, and ideally, I would like to have 1 column.
In order to load it with Data I made my ViewController the NSCollectionViewDataSource, and implemented the - (NSInteger)collectionView:(NSCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section and - (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView
itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath
UPDATED CODE
Full Code included:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[collectionView registerClass:ItemOne.class forItemWithIdentifier:#"Item1"];
[collectionView registerClass:ItemTwo.class forItemWithIdentifier:#"Item2"];
cellArray = [#[#"Item1", #"Item2", #"Item1", #"Item2", #"Item1"] mutableCopy];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
#pragma mark - NSCollectionViewDatasource -
- (NSInteger)collectionView:(NSCollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
// We are going to fake it a little. Since there is only one section
NSLog(#"Section: %ld, count: %ld", (long)section, [cellArray count]);
return [cellArray count];
}
- (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView
itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"IndexPath: %#, Requested one: %ld", indexPath, [indexPath item]);
NSLog(#"Identifier: %#", [cellArray objectAtIndex:[indexPath item]]);
NSCollectionViewItem *theItem = [collectionView makeItemWithIdentifier:[cellArray objectAtIndex:[indexPath item]] forIndexPath:indexPath];
return theItem;
}
UPDATE
The ItemOne and ItemTwo classes are both empty classes, the nib for each has a NSCollectionViewItem which in turn has a view, with label. The View is connected to the NSCollectionViewItem by the view property in NSCollectionViewItem. There are currently no constraints except for the default ones
The NSCollectionView grid is set up as follows:
Layout: Grid Dimensions: Max Rows: 0 Max Columns: 1 Min Item Size:
Width: 250 Height: 150 Max Item Size: Width: 250 Height: 150
This is the code for setting up the whole thing, at this point not tying it to a data source.
It seems that no matter what I change the settings or even changing the CollectionView type to Flow doesn't change anything, it looks the same.
I have been approaching this as an AutoLayout issue because originally there were some auto layout issues, but those have all been resolved.
Any help would be greatly appreciated.
The data array should hold data instead of NSCollectionViewItems. In collectionView:itemForRepresentedObjectAtIndexPath: you call makeItemWithIdentifier:forIndexPath:. Call registerClass:forItemWithIdentifier: or registerNib:forItemWithIdentifier: to register your class or nib.
More info in the documentation of NSCollectionView, collectionView:itemForRepresentedObjectAtIndexPath: and makeItemWithIdentifier:forIndexPath:.
EDIT:
There are two ways to provide a NSCollectionViewItem.
registerClass:forItemWithIdentifier:. When the collection view needs a new item, it instatiates this class. NSCollectionViewItem is a subclass of NSViewController and NSViewController looks for a nib with the same name as the class. The NSCollectionViewItem is the owner of the nib.
registerNib:forItemWithIdentifier:. When the collection view needs a new item, it loads this nib. The NSCollectionViewItem is a top level object in the nib.
You mixed registerClass:forItemWithIdentifier: with a xib for use with registerNib:forItemWithIdentifier:. Use registerNib:forItemWithIdentifier: or fix the xib.
I have it figured out.
And have made a github repo with a working version Working Version of Collection View Sample Application
First thing. Thanks to Willeke's catch of the way the original xib was setup I was able to get the Grid type working. But in the end the Grow view is a better type of view if you can make it do what you want, because it support sections, and distances between views etc. So eventhough I started out wanting to use the Grid type I am going to implement the Grow type in my app.
So I accomplished a single column view using the Grow type.
My Criteria for success are:
That it can support non-uniform view heights (Each custom view can have it's own height)
That there is a single column, and each custom view expands if the view size expands.
Onto the source code:
#interface ViewController ()
#property NSMutableArray *cellArray;
#property (weak) IBOutlet NSCollectionView *collectionView;
#end
#implementation ViewController
#synthesize cellArray;
#synthesize collectionView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[collectionView registerClass:ItemOne.class forItemWithIdentifier:#"Item1"];
[collectionView registerClass:ItemTwo.class forItemWithIdentifier:#"Item2"];
cellArray = [#[#"Item1", #"Item2", #"Item1", #"Item2", #"Item1"] mutableCopy];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
#pragma mark - NSCollectionViewDatasource -
- (NSInteger)numberOfSectionsInCollectionView:(NSCollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(NSCollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
// We are going to fake it a little. Since there is only one section
NSLog(#"Section: %ld, count: %ld", (long)section, [cellArray count]);
return [cellArray count];
}
- (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView
itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"IndexPath: %#, Requested one: %ld", indexPath, [indexPath item]);
NSLog(#"Identifier: %#", [cellArray objectAtIndex:[indexPath item]]);
NSCollectionViewItem *theItem = [collectionView makeItemWithIdentifier:[cellArray objectAtIndex:[indexPath item]] forIndexPath:indexPath];
theItem.representedObject = [cellArray objectAtIndex:[indexPath item]];
return theItem;
}
#pragma mark - NSCollectionViewDelegate -
- (NSSize)collectionView:(NSCollectionView *)collectionView
layout:(NSCollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%#", indexPath);
NSSize size = NSMakeSize(438, 150);
NSInteger width = 0;
NSInteger height = 0;
NSString *label = [cellArray objectAtIndex:[indexPath item]];
NSRect collectionFrame = [collectionView frame];
width = collectionFrame.size.width;
// TODO: This needs to be based on the actual value of the view instead of hardcoding a number in.
if ([label isEqualToString:#"Item1"]) {
height = 114;
} else if ([label isEqualToString:#"Item2"]) {
height = 84;
}
size = NSMakeSize(width, height);
return size;
}
#end
And there you have it. The implementation wasn't too bad. Each of the Custom views that show up in the NSCollectionView are defined in there own NSCollectionViewItem and .xib file, so they are easily modifiable.
The only part that is brittle is where I am calculating the height of each view, and it is only brittle because I am being lazy in my implementation in the sample application. In the actual implementation I will dynamically grab them from the actual views, so that they aren't tied to a static number.
I'm using iOS9 XCode7
I need to change the height of cell Dynamically according to labelText Height
I have used:
self.tableView.rowHeight=UITableViewAutomaticDimension;
But it is not working for custom made cell.
sizetoFit is removed in iOS9.
Please suggest something.
Thanks
Give your label constrains relative to the cell, top, bottom, left and right.
Than your cell size will grow with the content height.
also make your label multiline.(by setting Lines property to 0 in attribute inspector)
#pragma mark- Menu table Delegates
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
I'm not quite sure what this sentence means (a screenshot would've helped):
"I need to change the height of cell Dynamically according to labelText Height"
So, you have a UITableViewCell, containing a UILabel, and want each cell in your table to have a height depending on that cell's label ?
Here's the code I use.
In my UITableViewCell class, I'll define a static function to measure, and return the height of my .xib file:
#implementation MikesTableViewCell
...
+(CGSize)preferredSize
{
static NSValue *sizeBox = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Assumption: The XIB file name matches this UIView subclass name.
NSString* nibName = NSStringFromClass(self);
UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
// Assumption: The XIB file only contains a single root UIView.
UIView *rootView = [[nib instantiateWithOwner:nil options:nil] lastObject];
sizeBox = [NSValue valueWithCGSize:rootView.frame.size];
});
return [sizeBox CGSizeValue];
}
#end
Then, in my UIViewController class, I'll tell my UITableView to use this cell, find out it's height.
#property (nonatomic) float rowHeight;
UINib *cellNib = [UINib nibWithNibName:#"MikesTableViewCell" bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"CustomerCell"];
rowHeight = [MikesTableViewCell preferredSize].height;
...
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return (float)rowHeight;
}
Again, this might not be exactly what you're looking for, but I hope this helps.
try this.
it is so simple.
set this height in heightForRowAtIndexPath method
float vendorNameHeight = [vendorNameLbl.text heigthWithWidth:width andFont:[UIFont fontWithName:#"Signika-Light" size:21.0]];
then
UILabel*vendorNameLbl=[[UILabel alloc]initWithFrame:CGRectMake( 13, 11, 100, 22)];
vendorNameLbl.numberOfLines = 0;
[vendorNameLbl sizeToFitVertically];
Hi guys I'm new to Cocoa programming and I am getting always NSOffState whether I'm checking or unchecking an NSButtonCell (Check Box Cell in the UI dragged to a cell in an NSTableView).
I have a #property IBOutlet NSButtonCell *mySelection, connected to the respective UI and the following code.
- (void) tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MediaAComparar *media = [mediasRecesEnStock objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"seleccion"])
{
if ([mySelection state] == NSOnState)
{
[media setValue:object forKey:#"seleccion"];
NSLog(#"on state");
}
if ([mySelection state] == NSOffState)
{
[media setValue:object forKey:#"seleccion"];
NSLog(#"off state");
}
}
}
I never get the NSOnState to execute, the only NSLog message I get is: off state.
Can anyone give me some help?
Thanks!!
If you have one outlet ("mySelection") and multiple rows, which row did you think the outlet connects to? (Answer: none of them. You probably hooked it up to the prototype cell, which is never displayed or used directly.)
But no matter, you don't need to check the state before you set it. Assuming your other code is correct, you should be able to do something like:
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MediaAComparar *medium = [mediasRecesEnStock objectAtIndex:row];
if ([tableColumn.identifier isEqualToString:#"seleccion"])
medium.seleccion = object.booleanValue;
}
Less code is better code.
I'm running into issues with a UITableView that lags while scrolling when specific cells will move to superview.
I've written my own IPFormKit for an easy way to create beautiful input forms with different kind of inputViews without having to re-code everything manually for each form field / cell.
I've got a UITableViewController that initializes my IPFormKit and its fields.
The - (UITableViewCell *) cellForRowAtIndexPath:(NSIndexPath)indexPath; loads the dequeued custom cells (called IPFormTableViewCell) and assigns the IPFormField to each cell.
The custom UITableViewCell (IPFormTableViewCell) creates all (possibly) required inputViews (UITextField, UITextView, CustomUILabel) with a CGRectZero on initialization.
The matching inputView depending on the IPFormField's type (that was already inited as an iVar of the cell) is resized and added as a subview to the cell.contentView within.
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath)indexPath
For UITextField and CustomUILabel this works flawlessly, but when the inputView is a UITextView, the scrolling of the UITableView lags (slightly) noticable when this cell will be displayed for the first time.
When the cell will be displayed again later after scrolling a bit (even if the cell was reused and thus the UITextView removed and readded), there is no lag and scrolling is super smooth for those cells.
I'm running out of ideas what the reason for this lag could be.
Any idea is appreciated.
PS: The lag is noticable on both, iPhone 4 and iPhone 4S and is of almost exactly the same duration (so it should not be CPU related)
UITableViewController.m:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"IPFormFieldCell";
// Get Form Field for indexPath
IPFormField *formField = [self.form fieldAtIndexPath:indexPath];
IPTableViewCell *cell = (IPTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[IPTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.backgroundView = nil;
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
cell.selectedBackgroundView = nil;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
[cell assignFormField:formField];
return cell;
}
IPFormTableViewCell.m:
- (void) assignFormField:(IPFormField *)field:
- (void) assignFormField:(IPFormField *)field {
if (formField != nil) {
formField.inputView = nil; // unlink old field
}
self.formField = field;
// Change Field Label
[fieldLabel setText:[field label]];
// Add an Input View to the Field
UIView *labelView = nil;
UIView *inputView = nil;
switch (formField.type) {
case IPFormFieldTypeTextField:
{
labelView = fieldLabel;
UITextField *textField = inputTextField;
textField.delegate = (IPFormTextField *)formField;
textField.inputAccessoryView = [formField.form inputAccessoryView];
textField.placeholder = [self.formField stringFromValue:self.formField.defaultValue];
textField.keyboardType = [(IPFormTextField *)formField keyboardType];
if (self.formField.value == nil || [[self.formField stringFromValue:self.formField.value] isEqualToString:[self.formField stringFromValue:self.formField.defaultValue]]) {
textField.clearsOnBeginEditing = YES;
} else {
textField.text = [self.formField stringFromValue:self.formField.value];
textField.clearsOnBeginEditing = NO;
}
inputView = textField;
break;
}
case IPFormFieldTypeTextArea:
{
UITextView *textView = inputTextView;
textView.delegate = (IPFormTextArea *)formField;
textView.inputAccessoryView = [formField.form inputAccessoryView];
if (self.formField.value == nil || ![[self.formField stringFromValue:self.formField.value] length] > 0) {
textView.text = [self.formField stringFromValue:self.formField.defaultValue];
} else {
textView.text = [self.formField stringFromValue:self.formField.value];
}
inputView = textView;
break;
}
default:
break;
}
self.leftItem = labelView;
self.rightItem = inputView;
if (leftItem != nil) {
[self.contentView addSubview:leftItem];
}
if (rightItem != nil) {
[self.contentView addSubview:rightItem];
}
formField.inputView = rightItem;
}
Apparently, cellForRowAtIndexPath: of my dataSource made use of a field's property, that was set as #property (nonatomic, copy) instead of #property (nonatomic, readonly).
Now that I've fixed it, the scrolling isn't lagging anymore.
As I guessed, your problem here is with your custom controls. Yes, you are reusing the cell, but this doesn't give anything in your case, as every time you request for the cell, you are creating new custom control for each cell. My advise, you can create and keep your custom controls as an instance variables, and when required return them without many if-elses, or, you could create custom cells for your two cases, and keep them dequeued with different cell identifiers and reuse them. Good Luck!
I have a custom UITableViewCell which is displaying various attributes of a Person object (backed by Core Data) ... some labels, images etc. I currently force the whole tableview to reload whenever any property changes, and that's obviously not efficient. I know with KVO, I should be able to add a listener to a label in the cell that can listen for changes in the Person's properties. But I'm not sure how to implement it and can't find any examples.
Here's what I typically do in my UITableView's cellForRowAtIndexPath:
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
static NSString *simple = #"CustomCellId";
CustomCell *cell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:simple];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for (id findCell in nib )
{
if ( [findCell isKindOfClass: [CustomCell class]])
{
cell = findCell;
}
}
}
Person *managedObject = [self.someArray objectAtIndex: indexPath.row];
cell.namelabel.text = managedObject.displayName;
return cell;
}
The cell is hooked up in IB. I would want to detect when displayName changes, and update just the name label.
Thanks
The above answer is great for static cells. Using KVO for UITableViewCells still works with cell reuse. Add the observers you need when the cell is about to appear, and remove them when the cell is no longer displayed. The only trick is that Apple seems to be inconsistent about sending didEndDisplayingCell:, so observers need to be removed in two places on iOS 6.1
#implementation MyTableViewCell
#property MyTableViewController * __weak parentTVC;
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
((MyTableViewCell *)cell).parentTVC = self;
// Don't add observers, or the app may crash later when cells are recycled
}
- (void)tableView:(UITableView *)tableView
willDisplayCell:(HKTimelineCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Add observers
}
- (void)tableView:(UITableView *)tableView
didEndDisplayingCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self removeMyKVOObservers];
}
- (void)viewWillDisappear:(BOOL)animated
{
for (MyTableViewCell *cell in self.visibleCells) {
// note! didEndDisplayingCell: isn't sent when the entire controller is going away!
[self removeMyKVOObservers];
}
}
The following can occur if observers aren't cleaned up. The observer might try to notify whatever object is at that memory location, which may not even exist.
<NSKeyValueObservationInfo 0x1d6e4860> (
<NSKeyValueObservance 0x1d4ea9f0: Observer: 0x1d6c9540, Key path: someKeyPath, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1c5c7e60>
<NSKeyValueObservance 0x1d1bff10: Observer: 0x1d6c9540, Key path: someOtherKeyPath, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1c588290>)
For background, you probably want to read the Key-Value Observing and Key-Value Coding Guides, if you haven't already. Then review the NSKeyValueObserving category methods.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html
In a nutshell, you need to carefully manage adding and removing the observing object to the observed objects list of observers (pardon the seeming redundancy of that statement). You don't want to have an object going away with observers still registered, or you get complaints and possible other issues.
That said, you use -addObserver:keyPath:options:context to add an object as an observer. Context should be a statically declared string. The options argument controls what data you get back in your observation method (see below). The keyPath is the path of property names from the observed object to the observed property (this may traverse multiple objects, and will be updated when intermediate objects change, not just when the leaf property changes).
In your case, you could observe the label, and use the text keyPath, or the cell, and use the nameLabel.text key path. If the table view class were designed differently, you might observe the entire array of cells, but there is no such property on UITableView. The problem with observing the cell is that the table view might delete it at any time (if your design uses multiple cells that serve the same purpose in a variable-length list). If you know your cells are static, you can probably observe them without worry.
Once you have an observer registered, that observer must implement
-observeValueForKeyPath:ofObject:change:context:, confirm that the context matches (just compare the pointer value to your static string's address; otherwise, invoke super's implementation), then look into the change dictionary for the data you want (or just ask the object for it directly) and use it to update your model as you see fit.
There are many examples of KVO in sample code, including on Apple's developer site, and as part of the bindings samples on Malcolm Crawford (mmalc)'s site, but most of it is for Mac OS X, not iOS.
This works:
In configureCell:
[managedObject addObserver: cell forKeyPath: #"displayName" options:NSKeyValueObservingOptionNew context: #"Context"];
In CustomCell:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
Person *label = (Person *) object;
self.namelabel.text = [label valueForKey:#"displayName"];
}
In my case I added an observer to the custom cell label forKeyPath "text" with options (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld).
When observing the value for the keyPath I check to ensure the keyPath is the one I want, just as an extra measure and then I call my method for what ever operation I want to carry out on that label
e.g in my case
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Helpers
CGSize cellSize = self.contentView.frame.size;
CGRect sizerFrame = CGRectZero;
sizerFrame.origin.x = kDefaultUITableViewCellContentLeftInset;
sizerFrame.origin.y = kDefaultUITableViewCellContentTopInset;
// The Profile Image
CGRect imageFrame = CGRectMake(sizerFrame.origin.x, sizerFrame.origin.y, kDefaultProfilePictureSizeBWidth, kDefaultProfilePictureSizeBHeight);
self.userProfilePictureUIImageView = [[UIImageView alloc] initWithFrame:imageFrame];
[self.userProfilePictureUIImageView setImage:[UIImage imageNamed:#"placeholderImage"]];
[ApplicationUtilities formatViewLayer:self.userProfilePictureUIImageView withBorderRadius:4.0];
// adjust the image content mode based on the lenght of it's sides
CGSize avatarSize = self.userProfilePictureUIImageView.image.size;
if (avatarSize.width < avatarSize.height) {
[self.userProfilePictureUIImageView setContentMode:UIViewContentModeScaleAspectFill];
} else {
[self.userProfilePictureUIImageView setContentMode:UIViewContentModeScaleAspectFit];
}
CGFloat readStateSize = 10.0;
CGRect readStateFrame = CGRectMake((imageFrame.origin.x + imageFrame.size.width) - readStateSize, CGRectGetMaxY(imageFrame) + 4, readStateSize, readStateSize);
// Read State
self.readStateUIImageView = [[UIImageView alloc] initWithFrame:readStateFrame];
self.readStateUIImageView.backgroundColor = RGBA2UIColor(0.0, 157.0, 255.0, 1.0);
[ApplicationUtilities formatViewLayer:self.readStateUIImageView withBorderRadius:readStateSize/2];
sizerFrame.origin.x = CGRectGetMaxX(imageFrame) + kDefaultViewContentHorizontalSpacing;
// read just the width of the senders label based on the width of the message label
CGRect messageLabelFrame = sizerFrame;
messageLabelFrame.size.width = cellSize.width - (CGRectGetMinX(messageLabelFrame) + kDefaultViewContentHorizontalSpacing);
messageLabelFrame.size.height = kDefaultInitialUILabelHeight;
// Store the original frame for resizing
initialLabelFrame = messageLabelFrame;
self.messageLabel = [[UILabel alloc]initWithFrame:messageLabelFrame];
[self.messageLabel setBackgroundColor:[UIColor clearColor]];
[self.messageLabel setFont:[UIFont systemFontOfSize:14.0]];
[self.messageLabel setTextColor:[UIColor blackColor]];
[self.messageLabel setNumberOfLines:2];
[self.messageLabel setText:#""];
// Modify Sizer Frame for Message Date Label
sizerFrame = initialLabelFrame;
// Modify the y offset
sizerFrame.origin.y = CGRectGetMaxY(sizerFrame) + kDefaultViewContentVerticalSpacing;
// Message Date
self.messageDateLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.messageDateLabel setBackgroundColor:[UIColor clearColor]];
[self.messageDateLabel setFont:[UIFont systemFontOfSize:12.0]];
[self.messageDateLabel setTextColor:RGBA2UIColor(200.0, 200.0, 200.0, 1.0)];
[self.messageDateLabel setHighlightedTextColor:[UIColor whiteColor]];
[self.messageDateLabel setTextAlignment:NSTextAlignmentRight];
[self.messageDateLabel setNumberOfLines:1];
[self.messageDateLabel setText:#"Message Date"];
[self.messageDateLabel sizeToFit];
[self.contentView addSubview:self.userProfilePictureUIImageView];
[self.contentView addSubview:self.readStateUIImageView];
[self.contentView addSubview:self.messageDateLabel];
[self.contentView addSubview:self.messageLabel];
// Add KVO for all text labels
[self.messageDateLabel addObserver:self forKeyPath:#"text" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL];
[self.messageLabel addObserver:self forKeyPath:#"text" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL];
}
return self;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqual:#"text"]) {
[self resizeCellObjects];
}
}
-(void)resizeCellObjects
{
// Resize and reposition the message label
CGRect messageLabelFrame = initialLabelFrame;
self.messageLabel.frame = messageLabelFrame;
[self.messageLabel setNumberOfLines:2];
[self.messageLabel sizeToFit];
// Resize the messageDate label
CGRect messageDateFrame = initialLabelFrame;
messageDateFrame.origin.y = CGRectGetMaxY(self.messageLabel.frame) + kDefaultViewContentVerticalSpacing;
self.messageDateLabel.frame = messageDateFrame;
[self.messageDateLabel sizeToFit];
}