带有视图的滚动视图中的“NSRangeException” - 超出范围?

时间:2016-01-25 04:13:25

标签: ios nsmutablearray scrollview

以下是我的所有代码:

ViewController.m

#import "ViewController.h"
#import "LTInfiniteScrollView.h"


@interface ViewController ()<LTInfiniteScrollViewDelegate,LTInfiniteScrollViewDataSource>
@property (nonatomic,strong) LTInfiniteScrollView* scrollView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];



    // Do any additional setup after loading the view, typically from a nib.
}

-(void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSMutableArray *timeArray=[[NSMutableArray alloc] init];

    [timeArray addObject:@"5m"];
    [timeArray addObject:@"10m"];
    [timeArray addObject:@"15m"];
    [timeArray addObject:@"30m"];
    [timeArray addObject:@"45m"];
    [timeArray addObject:@"60m"];
    [timeArray addObject:@"90m"];
    [timeArray addObject:@"120m"];
    [timeArray addObject:@"240m"];
    _timeArray=[timeArray mutableCopy];
    NSLog(@"TIME_ARRAY:%@",_timeArray);
    self.scrollView = [[LTInfiniteScrollView alloc]initWithFrame:CGRectMake(0, 300, CGRectGetWidth(self.view.bounds), 200)];

    [self.scrollView setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:self.scrollView];
    //self.scrollView.delegate = self;
    self.scrollView.dataSource = self;
    [self.scrollView reloadData];
}

- (IBAction)reload:(id)sender {
    self.scrollView.delegate = nil;
    [self.scrollView reloadData];
}

- (IBAction)reloadWithFancyEffect:(id)sender {
    self.scrollView.delegate = self;
    [self.scrollView reloadData];

}

-(int) totalViewCount
{
    return _timeArray.count;
}
-(int) visibleViewCount
{
    return 5;
}

-(UIView*) viewAtIndex:(int)index reusingView:(UIView *)view;
{
    if(view){
        ((UILabel*)view).text = [_timeArray objectAtIndex:index];
        return view;
    }

    UILabel *aView = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 64, 64)];
    aView.backgroundColor = [UIColor blackColor];
    aView.layer.cornerRadius = 64 / 2;
    aView.layer.masksToBounds = YES;

    aView.backgroundColor = [UIColor colorWithRed:0/255.0 green:175/255.0 blue:240/255.0 alpha:1];

    aView.textColor = [UIColor whiteColor];
    aView.textAlignment = NSTextAlignmentCenter;

    NSLog(@"INDEX_CALLED:%d",index);
    aView.text =  [_timeArray objectAtIndex:index];

    return aView;
}

-(void) updateView:(UIView*) view withDistanceToCenter:(CGFloat)distance scrollDirection:(ScrollDirection)direction
{
    CGFloat percent = distance/CGRectGetWidth(self.view.bounds)*5;
    if(view.tag == 1){
        NSLog(@"%f",percent);
    }

    CATransform3D transform = CATransform3DIdentity;

    // scale
    CGFloat scale = 1.6 - 0.3*fabs(percent);
    transform = CATransform3DScale(transform, scale, scale, scale);

    // translate
    CGFloat translate = 16 * percent;
    transform = CATransform3DTranslate(transform,translate, 0, 0);

    // rotate
    if(fabs(percent)<1){
        CGFloat angle =  M_PI * percent;
        transform.m34 = 1.0/-600;
        transform = CATransform3DRotate(transform, angle , 0.0f, 1.0f, 0.0f);
    }

    view.layer.transform = transform;


}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

LTInfiniteScrollView.m

#import "LTInfiniteScrollView.h"

@interface LTInfiniteScrollView()<UIScrollViewDelegate>
@property CGSize viewSize;
@property(nonatomic,strong) UIScrollView *scrollView;
@property(nonatomic,strong) NSMutableArray* views;
@property(nonatomic) int visibleViewCount;
@property(nonatomic) int totalViewCount;
@property(nonatomic) CGFloat preContentOffsetX;
@property(nonatomic) CGFloat totalWidth;
@property int currentIndex;
@property BOOL dragging;
@property ScrollDirection scrollDirection;
@end


@implementation LTInfiniteScrollView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

-(void) setup
{
    self.scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))];
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.delegate = self;
    [self addSubview: self.scrollView];
}

-(void) reloadData
{

    for(UIView* view in self.views){
        [view removeFromSuperview];
    }

    self.visibleViewCount = [self.dataSource visibleViewCount];
    self.totalViewCount = [self.dataSource totalViewCount];

    CGFloat viewWidth = CGRectGetWidth(self.bounds)/self.visibleViewCount;
    CGFloat viewHeight = CGRectGetHeight(self.bounds);
    self.viewSize = CGSizeMake(viewWidth, viewHeight);

    self.totalWidth = viewWidth*self.totalViewCount;

    self.scrollView.contentSize=CGSizeMake(self.totalWidth, CGRectGetHeight(self.bounds));

    self.views = [NSMutableArray array];

    int begin = -ceil(self.visibleViewCount/2.0f);
    int end = ceil(self.visibleViewCount/2.0f);
    self.currentIndex = 0;

    self.scrollView.contentOffset = CGPointMake(self.totalWidth/2-CGRectGetWidth(self.bounds)/2, 0);

    CGFloat currentCenter = [self currentCenter].x;

    for(int i = begin; i<=end;i++){
        UIView* view = [self.dataSource viewAtIndex:i reusingView:nil];
        view.center = [self centerForViewAtIndex:i];
        view.tag = i;
        [self.views addObject:view];

        [self.scrollView addSubview:view];
        [self.delegate updateView:view withDistanceToCenter:(view.center.x - currentCenter) scrollDirection:self.scrollDirection];
    }

}

-(CGPoint) centerForViewAtIndex:(int) index
{
    CGFloat y = CGRectGetMidY(self.bounds);
    CGFloat x = self.totalWidth/2 + index*self.viewSize.width;
//    NSLog(@"view center:%f at index:%d",x,index);
    return CGPointMake(x, y);
}

-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat offset = [self currentCenter].x - self.totalWidth/2 ;
    self.currentIndex = ceil((offset- self.viewSize.width/2)/self.viewSize.width);

//    NSLog(@"--------------------------------");
    for (UIView* view in self.views) {
        if([self viewCanBeQueuedForReuse:view]){
            int indexNeeded;
            int indexOfViewToReuse = (int)view.tag;
            if(indexOfViewToReuse < self.currentIndex){
                indexNeeded = indexOfViewToReuse + self.visibleViewCount + 2;
            }else{
                indexNeeded = indexOfViewToReuse - (self.visibleViewCount + 2);
            }

            //NSLog(@"index:%d indexNeeded:%d",indexOfViewToReuse,indexNeeded);

            [view removeFromSuperview];

            UIView* viewNeeded = [self.dataSource viewAtIndex:indexNeeded reusingView:view];
            viewNeeded.center = [self centerForViewAtIndex:indexNeeded];
            [self.scrollView addSubview:viewNeeded];
            viewNeeded.tag = indexNeeded;
        };

        CGFloat currentCenter = [self currentCenter].x;
        [self.delegate updateView:view withDistanceToCenter:(view.center.x - currentCenter) scrollDirection:self.scrollDirection];

    }
    if(self.dragging){
        if(self.scrollView.contentOffset.x > self.preContentOffsetX){
            self.scrollDirection = ScrollDirectionLeft;
        }else{
            self.scrollDirection = ScrollDirectionRight;
        }
    }

    self.preContentOffsetX = self.scrollView.contentOffset.x;
}


-(CGPoint) currentCenter
{
    CGFloat x = self.scrollView.contentOffset.x + CGRectGetWidth(self.bounds)/2.0f;
    CGFloat y = self.scrollView.contentOffset.y;
    return  CGPointMake(x, y);
}

-(BOOL) viewCanBeQueuedForReuse:(UIView*) view
{

    CGFloat distanceToCenter = [self currentCenter].x - view.center.x;
    CGFloat threshold = (ceil(self.visibleViewCount/2.0f))*self.viewSize.width + self.viewSize.width/2.0f;

    if(self.scrollDirection == ScrollDirectionLeft){
        if(distanceToCenter <0){
            return NO;
        }
    }else{
        if(distanceToCenter >0){
            return NO;
        }
    }
    if(fabs(distanceToCenter)> threshold){
        return YES;
    }
    return NO;

}

-(void) scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    self.dragging = YES;
}

-(void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    self.dragging = NO;
    [self.scrollView setContentOffset:[self contentOffsetForIndex:self.currentIndex] animated:YES];
}

-(void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self.scrollView setContentOffset:[self contentOffsetForIndex:self.currentIndex] animated:YES];

}

-(CGPoint) contentOffsetForIndex:(int) index
{
    CGFloat x = self.totalWidth/2 + index*self.viewSize.width - CGRectGetWidth(self.bounds)/2;

    return CGPointMake(x, 0);
}

@end

这里有误解我的错误:

2016-01-24 23:08:13.335 LTInfiniteScrollView[21385:2910198] TIME_ARRAY:(
    5m,
    10m,
    15m,
    30m,
    45m,
    60m,
    90m,
    120m,
    240m
)
2016-01-24 23:08:13.356 LTInfiniteScrollView[21385:2910198] INDEX_CALLED:-3
2016-01-24 23:08:13.359 LTInfiniteScrollView[21385:2910198] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 18446744073709551613 beyond bounds [0 .. 8]'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001018e5f45 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010135ddeb objc_exception_throw + 48
    2   CoreFoundation                      0x00000001017c99e4 -[__NSArrayM objectAtIndex:] + 212
    3   LTInfiniteScrollView                0x0000000100e504e4 -[ViewController viewAtIndex:reusingView:] + 852
    4   LTInfiniteScrollView                0x0000000100e518e4 -[LTInfiniteScrollView reloadData] + 2340
    5   LTInfiniteScrollView                0x0000000100e4ff09 -[ViewController viewDidAppear:] + 1193
    6   UIKit                               0x0000000102288f3d -[UIViewController _setViewAppearState:isAnimating:] + 830
    7   UIKit                               0x000000010228bd1a __64-[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:]_block_invoke + 42
    8   UIKit                               0x0000000102289fc6 -[UIViewController _executeAfterAppearanceBlock] + 86
    9   UIKit                               0x00000001020fd01a _runAfterCACommitDeferredBlocks + 633
    10  UIKit                               0x00000001021100b6 _cleanUpAfterCAFlushAndRunDeferredBlocks + 95
    11  CoreFoundation                      0x0000000101811ffc __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
    12  CoreFoundation                      0x0000000101807c85 __CFRunLoopDoBlocks + 341
    13  CoreFoundation                      0x00000001018073e2 __CFRunLoopRun + 850
    14  CoreFoundation                      0x0000000101806e08 CFRunLoopRunSpecific + 488
    15  GraphicsServices                    0x0000000105c9fad2 GSEventRunModal + 161
    16  UIKit                               0x00000001020f1031 UIApplicationMain + 171
    17  LTInfiniteScrollView                0x0000000100e4f9ef main + 111
    18  libdyld.dylib                       0x000000010447292d start + 1
    19  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

1 个答案:

答案 0 :(得分:-1)

我认为你的问题存在于这一行:

UIView* view = [self.dataSource viewAtIndex:i reusingView:nil];

查看错误,似乎该行的最后一行不是iOS SDK或本机Objective-C库的一部分。它看起来像你正在寻找-3的索引,它不被识别为无符号整数,因此它被视为一个真正的,非常大的价值,并且在你的外面数据源数组边界。我建议您每次使用objectAtIndex:时添加一项检查,以确保您要查找的索引至少在数组范围内。