滚动时单元格元素错位 - CollectionView

Cell elements get misplaced when scrolling - CollectionView

我有一个自定义的 UICollectionView。看起来不错,一切就绪,但一旦我开始滚动,一切都搞砸了。单元格中的图像错位、尺寸错误等。

我的习惯 ViewController 看起来像这样

    @implementation E5BaseDashboardViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.horizontalPadding = 10.0f;
    self.verticalPadding = 10.0f;

    NSManagedObjectContext *context = [[E5RestKitManager sharedInstance] threadSafeManagedObjectContext];
    [context performBlockAndWait:^{
        self.collectionViewData = [[E5MenuPointController sharedInstance] rootEntityMenuPointsWithDrawerPosition:E5DrawerPositionCenter andContext:context];
    }];

    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
    self.collectionView.backgroundColor = [UIColor colorWithRed:0.965f green:0.961f blue:0.953f alpha:1.00f];

    // view is not hidden under navigation bar
    if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {
        self.edgesForExtendedLayout = UIRectEdgeNone;

    // we need to move collectionView by the size of navigation bar + padding
        self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, CGRectGetHeight(self.navigationController.navigationBar.frame) + 2 * self.verticalPadding, 0);
    }

    [self.collectionView registerClass:[E5DashboardItemCell class] forCellWithReuseIdentifier:cell_identifier];

    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;

    [self.view addSubview:self.collectionView];
}

#pragma mark -
#pragma mark E5BaseDashboardViewController Data Source

- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section;
{
    return [self.collectionViewData count];
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
    E5DashboardItemCell *cell = [cv dequeueReusableCellWithReuseIdentifier:cell_identifier forIndexPath:indexPath];
    E5MenuPoint *menuPoint = (E5MenuPoint *) [self.collectionViewData objectAtIndex:indexPath.row];

    [cell configureCellWithMenuPoint: menuPoint];

    if ([[E5MenuPointController sharedInstance] isCategoryMenuPoint: menuPoint])
    {
        [cell.imageView setHidden:YES];
        [cell.circleBorder setHidden: YES];
    }
    else
    {
        [cell.imageView setHidden: NO];
        [cell.circleBorder setHidden: NO];
    }

    return cell;
}

#pragma mark -
#pragma mark E5BaseDashboardViewController Delegate layout setup

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    E5MenuPoint *menuPoint = (E5MenuPoint *) [self.collectionViewData objectAtIndex:indexPath.row];

    if ([[E5MenuPointController sharedInstance] isCategoryMenuPoint: menuPoint])
    {
        //set header cell size
        CGSize cellFrame =  CGSizeMake(100, 100);
        cellFrame.height = (self.view.frame.size.width - 20) / 4;
        cellFrame.width = self.view.frame.size.width - 20;

        return cellFrame;
    }
    else
    {
        NSNumber *num = [NSNumber numberWithInt:56];
        [menuPoint setCounter_notification_total: num];

        //set tile cell size
        CGSize cellFrame =  CGSizeMake(100, 100);
        cellFrame.height = (self.view.frame.size.width/2 - 15) * 1.25f;
        cellFrame.width = self.view.frame.size.width/2 - 15;

        return cellFrame;
    }
}

#pragma mark -
#pragma mark E5BaseDashboardViewController Delegate insets, spacing

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    //set cell insets
    return UIEdgeInsetsMake(10, 10, 10, 10);
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    //set horizontal cell spacing
    return self.horizontalPadding;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    //set vertical cell spacing
    return self.verticalPadding;
}

#pragma mark -
#pragma mark E5BaseDashboardViewController Delegate selection

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [[E5MenuPointController sharedInstance] setNotificationCounterTo:0 forMenuPointWithReference:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] ref]];

    [E5ActionHandler performActionOfType:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] action_type]
                                    name:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] name]
                               reference:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] ref]
                         targetReference:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] action_target_ref]
                                  andUrl:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] action_url]
                          withStoryboard:self.storyboard
                     andDrawerController:self.mm_drawerController
                              backToRoot:YES];

    // track the action
    if([[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] action_ref] length] > 0)
    {
        [[E5TrackingController sharedInstance] trackActionWithReference:[(E5MenuPoint *)[self.collectionViewData objectAtIndex:indexPath.row] action_ref]];
    }
}

#pragma mark -
#pragma mark dealloc

- (void)dealloc
{
    _collectionView.delegate = nil;
    _collectionView.dataSource = nil;
}

@end

和自定义 DashboardItemCell class 看起来像这样:

#import "E5DashboardItemCell.h"
#import "E5MenuPoint.h"
#import "E5MenuPointController.h"

@implementation E5DashboardItemCell

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self)
    {
        // change to our custom selected background view
        self.backgroundColor = [UIColor whiteColor];
        self.layer.borderWidth = 1.0f;
        self.layer.borderColor = [UIColor colorWithRed:0.925f green:0.925f blue:0.925f alpha:1.00f].CGColor;

        _circleBorder = [CAShapeLayer layer];
        [_circleBorder setFrame: CGRectMake(30, 20, self.frame.size.width - 60, self.frame.size.width - 60)];
        [_circleBorder setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, _circleBorder.frame.size.width, _circleBorder.frame.size.height)] CGPath]];
        [_circleBorder setStrokeColor:[[UIColor colorWithRed:0.910f green:0.910f blue:0.898f alpha:1.00f] CGColor]];
        [_circleBorder setFillColor:[[UIColor clearColor] CGColor]];
        [self.layer addSublayer:self.circleBorder];

        CGFloat radius = self.circleBorder.frame.size.width/2;
        CGFloat positionNotificationCenter = sqrt(radius * radius/ 2);

        _notificationCircle = [CAShapeLayer layer];
        [_notificationCircle setFrame:CGRectMake(self.circleBorder.frame.origin.x + radius + positionNotificationCenter - radius/4,
                                                self.circleBorder.frame.origin.y + radius - positionNotificationCenter - radius/4,
                                                radius/2, radius/2)];
        [_notificationCircle setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, _notificationCircle.frame.size.width, _notificationCircle.frame.size.height)] CGPath]];
        [_notificationCircle setStrokeColor:[[UIColor whiteColor] CGColor]];
        [_notificationCircle setFillColor:[[UIColor colorWithRed:0.949f green:0.349f blue:0.196f alpha:1.00f] CGColor]];
        [self.layer addSublayer:self.notificationCircle];

        _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.circleBorder.frame.origin.x + self.circleBorder.frame.size.width/2 - self.circleBorder.frame.size.width/5,
                                                                   self.circleBorder.frame.origin.y + self.circleBorder.frame.size.height/2 - self.circleBorder.frame.size.height/5,
                                                                   self.circleBorder.frame.size.width/2.5,
                                                                   self.circleBorder.frame.size.height/2.5)];
        [self.layer addSublayer: self.imageView.layer];

        _counter = [[UILabel alloc] initWithFrame:CGRectMake(_notificationCircle.frame.origin.x, _notificationCircle.frame.origin.y, _notificationCircle.frame.size.width, _notificationCircle.frame.size.height)];
        _counter.textAlignment = NSTextAlignmentCenter;
        [_counter setFont:[UIFont boldSystemFontOfSize:15]];
        _counter.textColor = [UIColor whiteColor];
        [self.layer addSublayer: self.counter.layer];

        _textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
        [_textLabel setFont:[UIFont systemFontOfSize:15]];
        _textLabel.textAlignment=NSTextAlignmentCenter;
        _textLabel.numberOfLines = 0;
        _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
        _textLabel.textColor = [UIColor colorWithRed:0.392f green:0.392f blue:0.380f alpha:1.00f];
        [self.layer addSublayer: self.textLabel.layer];
    }
    return self;
}

- (void)configureCellWithMenuPoint:(E5MenuPoint *)menuPoint
{
    BOOL isCategory = [[E5MenuPointController sharedInstance] isCategoryMenuPoint:menuPoint];

    // -- text --
    self.textLabel.frame = isCategory ? CGRectMake(10, 5, self.frame.size.width - 20, self.frame.size.height - 10) : CGRectMake(10, self.circleBorder.frame.origin.y + self.circleBorder.frame.size.height + 10, self.frame.size.width - 20 , self.frame.size.height - self.circleBorder.frame.size.height - self.circleBorder.frame.origin.y - 20);
    self.textLabel.text = menuPoint.name;

    // -- image --
    if([menuPoint.image_small length] > 0 | [menuPoint.image_medium length] > 0 | [menuPoint.image_large length] > 0)
    {
        // show image
        if (self.imageView == nil) {
            self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(52.5f,42.5f,40,40)];
            [self addSubview:self.imageView];
        }

        // get the most appropriate image size for the display
        NSString *imageURLAsString = [NSString string];
        if([E5Utils isRetinaDisplay])
        {
            // retina display image priorities: medium, large, small
            if([menuPoint.image_medium length] > 0)
            {
                imageURLAsString = menuPoint.image_medium;
            }
            else if([menuPoint.image_large length] > 0)
            {
                imageURLAsString = menuPoint.image_large;
            }
            else if([menuPoint.image_small length] > 0)
            {
                imageURLAsString = menuPoint.image_small;
            }
        } else {
            // non-retina display image priorities: small, medium, large
            if([menuPoint.image_small length] > 0)
            {
                imageURLAsString = menuPoint.image_small;
            } else if([menuPoint.image_medium length] > 0)
            {
                imageURLAsString = menuPoint.image_medium;
            } else if([menuPoint.image_large length] > 0)
            {
                imageURLAsString = menuPoint.image_large;
            }
        }

        if([imageURLAsString length] > 0)
        {
            [self.imageView setImageWithURL:[NSURL URLWithString:imageURLAsString] placeholderImage:[UIImage imageNamed:@"drawer_item_icon"]];
        }
    }
    else
    {
        if(self.imageView != nil)
        {
            // hide imageView
            [self.imageView removeFromSuperview];
            self.imageView = nil;
        }
    }

    // -- counter --
    [self.counter setText:[NSString stringWithFormat:@"%@", menuPoint.counter_notification_total]];

    if (menuPoint.counter_notification_total.integerValue < 9)
    {
        [_counter setFont:[UIFont boldSystemFontOfSize:13]];
    }
    else if (menuPoint.counter_notification_total.integerValue > 9 && menuPoint.counter_notification_total.integerValue < 99)
    {
        [_counter setFont:[UIFont boldSystemFontOfSize:10]];
    }
    else if (menuPoint.counter_notification_total.integerValue > 99)
    {
        [_counter setFont:[UIFont boldSystemFontOfSize:7]];
    }

    BOOL showCounter = FEATURE_MENUITEM_COUNTERS && menuPoint.countWithPermanent > 0;

    if (!isCategory && showCounter) {
        [self.counter setHidden:NO];
        [self.notificationCircle setHidden: NO];
    } else {
        [self.counter setHidden:YES];
        [self.notificationCircle setHidden: YES];
    }
}

@end

我想这与细胞的重复使用有关,但我就是想不通是什么。这让我发疯。有谁知道可能出了什么问题?

非常感谢

成功了。您必须为各种类型的单元格设置单独的标识符。由于我只使用了一个标识符,但有 2 种类型的单元格,因此我的框架因仅重复使用一种类型的单元格而变得混乱。