Objective c 将KVO添加到UITableViewCell
我有一个自定义UITableViewCell,它显示Person对象的各种属性(由核心数据支持)。。。一些标签、图像等。我现在强迫整个tableview在任何属性更改时重新加载,这显然是没有效率的。我知道,使用KVO,我应该能够向单元格中的标签添加一个侦听器,该标签可以侦听个人属性的更改。但我不知道如何实现它,也找不到任何例子 以下是我通常在UITableView的CellForRowatineXpath中执行的操作:Objective c 将KVO添加到UITableViewCell,objective-c,core-data,uitableview,key-value-observing,Objective C,Core Data,Uitableview,Key Value Observing,我有一个自定义UITableViewCell,它显示Person对象的各种属性(由核心数据支持)。。。一些标签、图像等。我现在强迫整个tableview在任何属性更改时重新加载,这显然是没有效率的。我知道,使用KVO,我应该能够向单元格中的标签添加一个侦听器,该标签可以侦听个人属性的更改。但我不知道如何实现它,也找不到任何例子 以下是我通常在UITableView的CellForRowatineXpath中执行的操作: - (UITableViewCell *) tableView: (
- (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;
}
该单元连接在IB中。我希望检测displayName何时更改,并只更新名称标签。
感谢您提供的背景资料,如果您还没有阅读过键值观察和键值编码指南,您可能需要阅读这些指南。然后回顾NSKeyValueObserving类别方法 简而言之,您需要小心地将观察对象添加和删除到观察者的观察对象列表中(请原谅该语句看似冗余)。你不想让一个对象在观察员仍然注册的情况下消失,或者你会收到投诉和其他可能的问题 也就是说,您可以使用
-addObserver:keyPath:options:context
将对象添加为观察者。上下文应该是静态声明的字符串。options参数控制在观察方法中返回的数据(见下文)。keyPath是特性名称从观察对象到观察特性的路径(这可能会遍历多个对象,并且会在中间对象更改时更新,而不仅仅是在叶特性更改时更新)
在您的情况下,您可以观察标签,并使用text
keyPath或单元格,然后使用namelab.text
keyPath。如果table view类的设计不同,则可以观察整个单元格数组,但UITableView上没有此类属性。观察单元格的问题是,表视图可能随时删除它(如果您的设计在可变长度列表中使用多个用于相同目的的单元格)。如果你知道你的细胞是静止的,你可以毫无顾虑地观察它们
注册了观察者后,该观察者必须实现
-observeValueForKeyPath:ofObject:change:context:
,确认上下文匹配(只需将指针值与静态字符串的地址进行比较;否则,调用super的实现),然后在更改字典中查找所需的数据(或直接向对象请求)并使用它来更新您认为合适的模型
示例代码中有许多KVO示例,包括苹果的开发者网站,以及Malcolm Crawford(mmalc)网站上绑定示例的一部分,但大多数示例都是针对Mac OS X的,而不是iOS。这可以:
在configureCell中:
[managedObject addObserver: cell forKeyPath: @"displayName" options:NSKeyValueObservingOptionNew context: @"Context"];
在自定义单元格中:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
Person *label = (Person *) object;
self.namelabel.text = [label valueForKey:@"displayName"];
}
上述答案对于静态电池来说是非常好的。将KVO用于
UITableViewCell
s仍然可以实现单元重用。在单元格即将显示时添加所需的观察者,并在单元格不再显示时删除它们。唯一的诀窍是,苹果在发送DiEndDisplayingCell:时似乎前后不一致,因此需要在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];
}
}
如果未清理观察者,可能会发生以下情况。观察者可能会尝试通知该内存位置上的任何对象,这些对象甚至可能不存在
(
)
在我的例子中,我使用选项(NSKeyValueObservingOptionNew
|NSKeyValueObservingOptionOld
)向自定义单元格标签forKeyPath“text”添加了一个观察者
当观察键路径的值时,我检查以确保键路径是我想要的,这是一个额外的度量,然后我调用我的方法来执行我想要在该标签上执行的任何操作
e、 g就我而言
-(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];
}
-(id)initWithStyle:(UITableViewCellStyle)样式重用标识符:(NSString*)重用标识符
{
self=[super-initWithStyle:style-reuseIdentifier:reuseIdentifier];
如果(自我){
//助手
CGSize cellSize=self.contentView.frame.size;
CGRect sizerFrame=CGRectZero;
sizerFrame.origin.x=kDefaultUITableViewCellContentLeftInset;
sizerFrame.origin.y=kDefaultUITableViewCellContentTopInset;
//配置文件图像
CGRect imageFrame=CGRectMake(sizerFrame.origin.x,sizerFrame.origin.y,kDefaultProfilePictureSizeBWidth,kDefaultProfilePictureSizeBHeight);
self.userProfilePictureUIImageView=[[UIImageView alloc]initWithFrame:imageFrame];
[self.userProfilePictureUIImageView设置图像:[UIImage ImageName:@“Placeholder图像”];
[ApplicationUtilities formatViewLayer:self.userProfilePictureUIImageView with BorderRadius:4.0];
//根据图像侧面的长度调整图像内容模式
CGSize avatarSize=self.userProfilePictureUIImageView.image.size;
if(虚拟化宽度<虚拟化高度){
[self.userProfilePictureUIImageView设置内容模式:UIViewContentModeScaleAspectFill];
}否则{
[self.userProfilePictureUIImageView设置内容模式:UIViewContentModeScaleAspectFit];
}
CGFloat readStateSize=10.0;
CGRect readStateFrame=CGRectMake((imageFrame.origin.x+imageFrame.size.width)-readStateSize,CGRectGetMaxY(imageFrame)+4,readStateSize,readStateSize);
//读取状态
self.readStateUIImageView=[[UIImageView alloc]initWithFrame:readStateFrame];
self.readStateUIImageView.backgroundColor=RGBA2UIColor(0.0157.0255.0,1.0);
[ApplicationUtility formatViewLayer:self.readStateUIImageView with borderRadius:readStateSize/2];
sizerFrame.origin.x=CGRectGetMaxX(imageFrame)+kDefaultViewContentHorizontalSpacing;
//仅根据邮件标签的宽度读取发件人标签的宽度
CGRect messageLabelFrame=siz