简体   繁体   English

UITableViewCell中的自定义UIView的drawRect的错误行为

[英]Wrong behavior of drawRect of custom UIView inside UITableViewCell

I'm facing a bit complicated (at least it looks like it to me) problem with a custom UIView that I made (called EventBadge ). 我制作的自定义UIView (称为EventBadge )面临一些复杂的问题(至少在我看来是这样)。

Here's the code of my custom class: 这是我的自定义类的代码:

EventBadge.h EventBadge.h

@interface EventBadge : UIView

- (void)setBadgeFillColor:(UIColor *) color;
- (void)setBadgeBorderColor:(UIColor *) color;
- (void)setBadgeIcon:(MyCustomIcons) icon;

@end

EventBadge.m EventBadge.m

@implementation EventBadge

UIColor *badgeFillColor;
UIColor *badgeBorderColor;
MyCustomIcons badgeIcon;

- (void)drawRect:(CGRect)rect {

    // Gets graphic context
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Sets fill and border colors for cirlce
    CGContextSetFillColor(context, CGColorGetComponents([badgeFillColor CGColor]));
    CGContextSetStrokeColor(context, CGColorGetComponents([badgeBorderColor CGColor]));

    // Set border line width
    CGContextSetLineWidth(context, 2.0);

    // Set rect containing circle as inset of rect
    CGRect circle = CGRectInset(rect, 1, 1);

    // Draw fill and stroke into rect
    CGContextFillEllipseInRect(context, circle);
    CGContextStrokeEllipseInRect(context, circle);

    // Draws icon
    [self drawBadgeIconInside:circle];

    // Fill graphic context with path
    CGContextFillPath(context);
}

/**
 * Sets the background color for the badge and forces refresh
 */
- (void)setBadgeFillColor:(UIColor *) color{
    badgeFillColor = color;
    [self setNeedsDisplay];
}

/**
 * Sets the background color for the badge and forces refresh
 */
- (void)setBadgeBorderColor:(UIColor *) color{
    badgeBorderColor = color;
    [self setNeedsDisplay];
}

/**
 * Sets the icon for the badge and forces refresh
 */
- (void)setBadgeIcon:(MyCustomIcons) icon{
    badgeIcon = icon;
    [self setNeedsDisplay];
}

/**
 * Draws the badge icon inside a rectangle
 */
- (void)drawBadgeIconInside:(CGRect) rect {

    // Creates the inner rectangle from the original one (20x20)
    CGRect iconContainer = CGRectInset(rect, 5, 5);

    // Switch on badgeIcon: many different supported types
    switch (badgeIcon) {
        case EventLocation:
            [StyleKit drawIconLocationWithFrame:iconContainer colorBase:[StyleKit blackMP]];
            break;
        case EventCar:
            [StyleKit drawIconCarWithFrame:iconContainer colorBase:[StyleKit blackMP]];
            break;
        default:
            MyLog(MyLogLevelError, @"INVALID MyCustomIcon");
            break;
    }
}

@end

I have a UITableView that can be filled with three different types of UITableViewCell , let's say TypeA , TypeB and TypeC . 我有一个UITableView可以填充三种不同类型的UITableViewCell ,比如TypeATypeBTypeC

TypeA and TypeB have different elements inside ( UILabels , UIViews and so on) and they both have my EventBadge . 类型A类型B有内(UILabels,UIViews等)不同的元素和他们都有我的EventBadge。 TypeC is made of standard elements only. TypeC仅由标准元素制成。

Here's the code for all cell types: 这是所有单元格类型的代码:

TypeA.h A型

@interface TypeACell : UITableViewCell

@property (strong, nonatomic) IBOutlet UIView *prevRouteView;
@property (strong, nonatomic) IBOutlet UIView *nextRouteView;
@property (strong, nonatomic) IBOutlet UILabel *addressLabel;
@property (strong, nonatomic) IBOutlet EventBadge *eventBadgeView;

@end

TypeB.h B型

@interface TypeBCell : UITableViewCell

@property (strong, nonatomic) IBOutlet EventBadge *eventBadgeView;
@property (strong, nonatomic) IBOutlet UIView *prevRouteView;
@property (strong, nonatomic) IBOutlet UIView *nextRouteView;
@property (strong, nonatomic) IBOutlet UILabel *titleLabel;
@property (strong, nonatomic) IBOutlet UILabel *addressLabel;
@property (strong, nonatomic) IBOutlet UILabel *startTime;
@property (strong, nonatomic) IBOutlet UILabel *endTime;
@property (strong, nonatomic) IBOutlet CalendarColorView *calendarColor;

@end

TypeC.h C型

@interface TypeCCell : UITableViewCell

@property (strong, nonatomic) IBOutlet UIView *routeView;
@property (strong, nonatomic) IBOutlet UILabel *duration;
@property (strong, nonatomic) IBOutlet UILabel *startTime;
@property (strong, nonatomic) IBOutlet UILabel *endTime;
@property (strong, nonatomic) IBOutlet CalendarColorView *calendarColor;
@property (strong, nonatomic) IBOutlet TransportTypeIconView *transportTypeView;

@end

I choose the type of cell inside cellForRowAtIndexPath method of my ViewController looking at the type of object stored in _tableviewData (the array used to fill the tableView). 我在ViewController的cellForRowAtIndexPath方法中选择单元格的类型,查看_tableviewData(用于填充tableView的数组)中存储的对象的类型。 The code looks like this: 代码如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if([_tableviewData[indexPath.row] isKindOfClass:[EventTypeA class]]){
        EventTypeA *event = (EventTypeA *)_tableviewData[indexPath.row];
        return [self tableView:tableView createTypeACell:event atIndexPath:indexPath];
    } 

    else if([_tableviewData[indexPath.row] isKindOfClass:[EventTypeB class]]) {
        EventTypeB *event = (EventTypeB *)_tableviewData[indexPath.row];
        return [self tableView:tableView createTypeBCell:event atIndexPath:indexPath];
    } 

    else {
        EventTypeC *event = (EventTypeC *)_tableviewData[indexPath.row];
        return [self tableView:tableView createTypeCCell:event atIndexPath:indexPath];
    } 
}

Inside each method createTypeXCell I work directly on elements and set their properties. 在每种方法的createTypeXCell内部,我直接处理元素并设置其属性。 Everything is working as expected except properties set on my custom view. 除了在我的自定义视图上设置的属性以外,所有其他功能均按预期工作。 So TypeC works perfectly and everything in TypeA and TypeB works as expected except the settings for colors and icons on my eventBadgeView . 因此, TypeC可以完美工作,并且TypeATypeB中的所有内容都可以按预期工作, 除了 eventBadgeView上的颜色和图标设置。

The behaviour that I get is that each eventBadgeView , no matter which UITableViewCell belongs to, gets painted with the properties of the last eventBadgeView being worked (the last item of the array). 我得到的行为是,无论哪个UITableViewCell属于每个eventBadgeView ,都使用正在工作的最后一个eventBadgeView的属性(数组的最后一项)绘制。

If I scroll a little bit up or down the UITableView , enough to render one item, that item gets updated well, with the properties I set previously. 如果我向上或向下滚动UITableView足以渲染一个项目,则该项目将得到很好的更新,具有我之前设置的属性。 But if I scroll too much everything gets messed up once again. 但是,如果我滚动太多,一切都会再次混乱。

I've noticed that drawRect gets always called a lot later with regards to setNeedsDisplay and I've learned that this is meant to be like this. 我已经注意到,关于setNeedsDisplay,drawRect总是会在以后被调用很多,而且我了解到这就是这样。

I've read on lots of SO posts (I'm not linking here all of them) and based on those what I've tried to do (with no luck) is: 我已经阅读了很多SO帖子(我没有在此处链接所有文章),基于这些我试图做的(没有运气)是:

  1. call [cell.eventBadgeView setNeedsDisplay] inside the method that creates the cell after setting properties 设置属性后,在创建单元格的方法内调用[cell.eventBadgeView setNeedsDisplay]
  2. put all the part of setting cell properties and [cell.eventBadgeView setNeedsDisplay] inside dispatch_async 将设置单元格属性和[cell.eventBadgeView setNeedsDisplay]的所有部分放入dispatch_async中
  3. use a CALayer to "force" drawRect to be executed synchronously 使用CALayer来“强制” drawRect以同步执行

Maybe as I'm new to ObjectiveC I'm missing some basic things, and I have big doubts on my custom EventBadge : UIView class since everything else works fine. 也许由于我不是ObjectiveC的新手,所以我错过了一些基本的知识,并且对自定义EventBadge:UIView类有很大的疑问,因为其他一切都可以正常工作。

Thanks in advance for the help! 先谢谢您的帮助! :) :)

You should declare those variables out the implementation body otherwise, they'll be threated like gloabal variables in the .m file (more info about this here ) 你应该申报,否则出了执行机构的变量,他们将threated像.m文件gloabal变量(更多这方面的信息在这里

UIColor *badgeFillColor;
UIColor *badgeBorderColor;
MyCustomIcons badgeIcon;

put them in an interface (inside the .m file or directly in the .h) and declare them as @property 将它们放入接口(在.m文件内部或直接在.h中)并声明为@property

@interface MPEventBadge ()

@property (strong, nonatomic) UIColor *badgeFillColor;
@property (strong, nonatomic) UIColor *badgeBorderColor;
@property (nonatomic) MPInsertEventIcons badgeIcon;

@end

you can then access the variable like 然后您可以像访问变量

_badgeFillColor = color;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM