diff --git a/LazyScrollView/LazyScrollView.xcodeproj/project.pbxproj b/LazyScrollView/LazyScrollView.xcodeproj/project.pbxproj index 7d03221..7f48aac 100644 --- a/LazyScrollView/LazyScrollView.xcodeproj/project.pbxproj +++ b/LazyScrollView/LazyScrollView.xcodeproj/project.pbxproj @@ -295,7 +295,7 @@ INFOPLIST_FILE = LazyScrollView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 7.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.xiabob.LazyScrollView; + PRODUCT_BUNDLE_IDENTIFIER = xiabob.LazyScrollView; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -308,7 +308,7 @@ INFOPLIST_FILE = LazyScrollView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 7.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.xiabob.LazyScrollView; + PRODUCT_BUNDLE_IDENTIFIER = xiabob.LazyScrollView; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -332,6 +332,7 @@ 743FAFBD1E0BBA54008097E9 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/LazyScrollView/LazyScrollView/Classes/LazyScrollView.h b/LazyScrollView/LazyScrollView/Classes/LazyScrollView.h index 8b59119..94c30bd 100644 --- a/LazyScrollView/LazyScrollView/Classes/LazyScrollView.h +++ b/LazyScrollView/LazyScrollView/Classes/LazyScrollView.h @@ -21,11 +21,11 @@ typedef NS_ENUM(NSUInteger, LazyScrollViewDirection) { @protocol LazyScrollViewDataSource @required -// ScrollView一共展示多少个item +/// ScrollView一共展示多少个item - (NSUInteger)numberOfItemInScrollView:(LazyScrollView *)scrollView; -// 要求根据index直接返回RectModel +/// 要求根据index直接返回RectModel - (LSVRectModel *)scrollView:(LazyScrollView *)scrollView rectModelAtIndex:(NSUInteger)index; -// 返回下标所对应的view +/// 返回下标所对应的view - (UIView *)scrollView:(LazyScrollView *)scrollView itemByLsvId:(NSString *)lsvId; @end @@ -35,7 +35,8 @@ typedef NS_ENUM(NSUInteger, LazyScrollViewDirection) { @protocol LazyScrollViewDelegate @optional -- (void)scrollView:(LazyScrollView *)scrollView didClickItemAtIndex:(NSUInteger)index; +///点击了对应小标的cell +- (void)scrollView:(LazyScrollView *)scrollView didClickItemAtIndex:(NSUInteger)index withLsvId:(NSString *)lsvId; @end @@ -63,10 +64,10 @@ typedef NS_ENUM(NSUInteger, LazyScrollViewDirection) { #pragma mark - LSVRectModel @interface LSVRectModel : NSObject -// view转换后的绝对值rect +/// view转换后的绝对值rect @property (nonatomic, assign) CGRect absRect; -// 业务下标,如果初始化时没有提供,LSVRectModel内部会自动生成 +/// 业务下标,如果初始化时没有提供,LSVRectModel内部会自动生成 @property (nonatomic, copy) NSString *lsvId; + (instancetype)modelWithRect:(CGRect)rect; diff --git a/LazyScrollView/LazyScrollView/Classes/LazyScrollView.m b/LazyScrollView/LazyScrollView/Classes/LazyScrollView.m index 6a4649b..ffe7bfa 100644 --- a/LazyScrollView/LazyScrollView/Classes/LazyScrollView.m +++ b/LazyScrollView/LazyScrollView/Classes/LazyScrollView.m @@ -79,7 +79,7 @@ - (instancetype)init { - (void)layoutSubviews { [super layoutSubviews]; - // TOOD: 此处算法待优化 + NSMutableArray *newVisibleViews = [self getVisiableViewModels].mutableCopy; NSMutableArray *newVisibleLsvIds = [newVisibleViews valueForKey:@"lsvId"]; NSMutableArray *removeViews = [NSMutableArray array]; @@ -115,8 +115,9 @@ - (void)layoutSubviews { - (void)reloadData { [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; [self.visibleViews removeAllObjects]; + [self.reuseViewPool removeAllObjects]; - [self updateAllDatas]; + [self updateModelDatas]; } - (void)enqueueReusableView:(UIView *)view { @@ -158,11 +159,13 @@ - (void)registerClass:(Class)viewClass forViewReuseIdentifier:(NSString *)identi #pragma mark - Utils - (CGFloat)minEdgeOffset { + //for UIScrollView the bounds cahnge when scroll CGFloat min = CGRectGetMinY(self.bounds); return MAX(min - kBufferSize, 0); } - (CGFloat)maxEdgeOffset { + //for UIScrollView the bounds cahnge when scroll CGFloat max = CGRectGetMaxY(self.bounds); return MIN(max + kBufferSize, self.contentSize.height); } @@ -174,7 +177,7 @@ - (LSVRectModel *)findFirstAscendModelWithMinEdge:(CGFloat)minEdge { NSInteger midIndex = (minIndex + maxIndex) / 2; LSVRectModel *model = self.allAscendingRectModels[midIndex]; - while (minIndex < maxIndex - 1) { + while (minIndex < midIndex && midIndex < maxIndex) { if (CGRectGetMinY(model.absRect) > minEdge) { maxIndex = midIndex; } @@ -185,9 +188,14 @@ - (LSVRectModel *)findFirstAscendModelWithMinEdge:(CGFloat)minEdge { model = self.allAscendingRectModels[midIndex]; } - midIndex = MAX(midIndex - 1, 0); + //处理多个view的y值相同的情况 + LSVRectModel *limitModel = model; + while (midIndex > 0 && CGRectGetMinY(model.absRect) == CGRectGetMinY(limitModel.absRect)) { + midIndex = MAX(midIndex - 1, 0); + model = self.allAscendingRectModels[midIndex]; + } - return self.allAscendingRectModels[midIndex]; + return model; } - (LSVRectModel *)findFirstDescendModelWithMaxEdge:(CGFloat)maxEdge { @@ -197,7 +205,7 @@ - (LSVRectModel *)findFirstDescendModelWithMaxEdge:(CGFloat)maxEdge { NSInteger midIndex = (minIndex + maxIndex) / 2; LSVRectModel *model = self.allDescendingRectModels[midIndex]; - while (minIndex < maxIndex - 1) { + while (minIndex < midIndex && midIndex < maxIndex) { if (CGRectGetMaxY(model.absRect) < maxEdge) { maxIndex = midIndex; } @@ -208,9 +216,14 @@ - (LSVRectModel *)findFirstDescendModelWithMaxEdge:(CGFloat)maxEdge { model = self.allDescendingRectModels[midIndex]; } - midIndex = MAX(midIndex - 1, 0); + //处理多个view的y值相同的情况 + LSVRectModel *limitModel = model; + while (midIndex > 0 && CGRectGetMaxY(model.absRect) == CGRectGetMaxY(limitModel.absRect)) { + midIndex = MAX(midIndex - 1, 0); + model = self.allDescendingRectModels[midIndex]; + } - return self.allDescendingRectModels[midIndex]; + return model; } - (NSArray *)getVisiableViewModels { @@ -222,14 +235,12 @@ - (NSArray *)getVisiableViewModels { LSVRectModel *firstDescendModel = [self findFirstDescendModelWithMaxEdge:[self maxEdgeOffset]]; NSInteger firstIndex = [self.allAscendingRectModels indexOfObject:firstAscendModel]; - firstIndex = MAX(firstIndex-1, 0); NSInteger lastIndex = [self.allAscendingRectModels indexOfObject:firstDescendModel]; - lastIndex = MIN(lastIndex+1, self.allAscendingRectModels.count-1); return [self.allAscendingRectModels subarrayWithRange:NSMakeRange(firstIndex, lastIndex-firstIndex+1)]; } -- (void)updateAllDatas { +- (void)updateModelDatas { [self.allRectModels removeAllObjects]; self.allAscendingRectModels = nil; self.allDescendingRectModels = nil; @@ -250,9 +261,9 @@ - (void)handleTapAction:(UIGestureRecognizer *)gestureRecognizer { CGPoint tapPoint = [gestureRecognizer locationInView:self]; for (LSVRectModel *model in visibleViews) { if (CGRectContainsPoint(model.absRect, tapPoint)) { - if ([self.delegate respondsToSelector:@selector(scrollView:didClickItemAtIndex:)]) { + if ([self.delegate respondsToSelector:@selector(scrollView:didClickItemAtIndex:withLsvId:)]) { NSInteger index = [self.allRectModels indexOfObject:model]; - [self.delegate scrollView:self didClickItemAtIndex:index]; + [self.delegate scrollView:self didClickItemAtIndex:index withLsvId:model.lsvId]; } break; @@ -260,7 +271,6 @@ - (void)handleTapAction:(UIGestureRecognizer *)gestureRecognizer { } } - #pragma mark - Setter - (void)setDataSource:(id)dataSource { @@ -292,7 +302,13 @@ - (NSMutableArray *)allAscendingRectModels { //升序 _allAscendingRectModels = [[self.allRectModels sortedArrayUsingComparator:^NSComparisonResult(LSVRectModel *obj1, LSVRectModel *obj2) { - return CGRectGetMinY(obj1.absRect) < CGRectGetMinY(obj2.absRect) ? NSOrderedAscending : NSOrderedDescending;} + CGFloat y1 = CGRectGetMaxY(obj1.absRect); CGFloat x1 = CGRectGetMinX(obj1.absRect); + CGFloat y2 = CGRectGetMaxY(obj2.absRect); CGFloat x2 = CGRectGetMinX(obj2.absRect); + if (y1 == y2) { + return x1 <= x2 ? NSOrderedAscending: NSOrderedDescending; + } else { + return y1 < y2 ? NSOrderedAscending : NSOrderedDescending; + } } ] mutableCopy]; } @@ -304,7 +320,14 @@ - (NSMutableArray *)allDescendingRectModels { //需要降序,而sortedArrayUsingComparator的结果是ascending order,所以block里面的结果是相反的。 _allDescendingRectModels = [[self.allRectModels sortedArrayUsingComparator:^NSComparisonResult(LSVRectModel *obj1, LSVRectModel *obj2) { - return CGRectGetMaxY(obj1.absRect) < CGRectGetMaxY(obj2.absRect) ? NSOrderedDescending : NSOrderedAscending;} + CGFloat y1 = CGRectGetMaxY(obj1.absRect); CGFloat x1 = CGRectGetMinX(obj1.absRect); + CGFloat y2 = CGRectGetMaxY(obj2.absRect); CGFloat x2 = CGRectGetMinX(obj2.absRect); + if (y1 == y2) { + return x1 <= x2 ? NSOrderedDescending : NSOrderedAscending; + } else { + return y1 < y2 ? NSOrderedDescending : NSOrderedAscending; + } + } ] mutableCopy]; } @@ -337,6 +360,7 @@ - (UITapGestureRecognizer *)tapGesture { @end +#pragma mark - LSVRectModel @implementation LSVRectModel diff --git a/LazyScrollView/LazyScrollView/ViewController.m b/LazyScrollView/LazyScrollView/ViewController.m index 1df33d2..2c37a8f 100644 --- a/LazyScrollView/LazyScrollView/ViewController.m +++ b/LazyScrollView/LazyScrollView/ViewController.m @@ -24,7 +24,7 @@ - (void)loadView { [super loadView]; [self loadDatas]; - [self setupUI]; + [self configViews]; } - (void)viewDidLoad { @@ -32,7 +32,47 @@ - (void)viewDidLoad { // Do any additional setup after loading the view, typically from a nib. } -- (void)setupUI { +- (void)loadDatas { + + NSMutableArray *array = @[].mutableCopy; + NSMutableDictionary *dictionary = @{}.mutableCopy; + + NSMutableArray *rectArray = [[NSMutableArray alloc] init]; + //Create a single column layout with 5 elements; + for (int i = 0; i < 500 ; i++) { + [rectArray addObject:[NSValue valueWithCGRect:CGRectMake(10, i *80 + 2 , self.view.bounds.size.width-20, 80-2)]]; + } + //Create a double column layout with 10 elements; + for (int i = 0; i < 1000 ; i++) { + [rectArray addObject:[NSValue valueWithCGRect:CGRectMake((i%2)*self.view.bounds.size.width/2 + 3, 41000 + i/2 *80 + 2 , self.view.bounds.size.width/2 -3, 80 - 2)]]; + } + //Create a trible column layout with 15 elements; + for (int i = 0; i < 1500 ; i++) { + NSUInteger row = 5; + [rectArray addObject:[NSValue valueWithCGRect:CGRectMake((i%row)*self.view.bounds.size.width/row + 1, 82000 + i/row *80 + 2 , self.view.bounds.size.width/row -4, 80 - 2)]]; + } + + for (NSInteger index = 0; index < rectArray.count; ++ index) { + NSString *lsvId = [NSString stringWithFormat:@"%@/%@", @(index / 10), @(index % 10)]; + LSVRectModel *model = [LSVRectModel modelWithRect:[(NSValue *)(rectArray[index]) CGRectValue] lsvId:lsvId]; + [array addObject:model]; + [dictionary setObject:lsvId forKey:lsvId]; + } + + + // for (NSInteger index = 0; index < 5000; ++ index) { + // NSString *lsvId = [NSString stringWithFormat:@"%@/%@", @(index / 10), @(index % 10)]; + // CGFloat width = ([UIScreen mainScreen].bounds.size.width - 30) / 2; + // LSVRectModel *model = [LSVRectModel modelWithRect:CGRectMake(10 + (index % 2) * (width+10), (index / 2) * (width+10), width, width) lsvId:lsvId]; + // [array addObject:model]; + // [dictionary setObject:lsvId forKey:lsvId]; + // } + self.rectDatas = array; + self.viewsData = dictionary; + +} + +- (void)configViews { self.view.backgroundColor = [UIColor whiteColor]; @@ -46,6 +86,8 @@ - (void)didReceiveMemoryWarning { // Dispose of any resources that can be recreated. } +#pragma mark - LazyScrollViewDataSource + - (NSUInteger)numberOfItemInScrollView:(LazyScrollView *)scrollView { return self.rectDatas.count; } @@ -78,24 +120,12 @@ - (UIView *)scrollView:(LazyScrollView *)scrollView itemByLsvId:(NSString *)lsvI return view; } -- (void)scrollView:(LazyScrollView *)scrollView didClickItemAtIndex:(NSUInteger)index { - NSLog(@"didClickItemAtIndex:%@", @(index)); -} +#pragma mark - LazyScrollViewDelegate -- (void)loadDatas { - - NSMutableArray *array = @[].mutableCopy; - NSMutableDictionary *dictionary = @{}.mutableCopy; - for (NSInteger index = 0; index < 5000; ++ index) { - NSString *lsvId = [NSString stringWithFormat:@"%@/%@", @(index / 10), @(index % 10)]; - CGFloat width = ([UIScreen mainScreen].bounds.size.width - 30) / 2; - LSVRectModel *model = [LSVRectModel modelWithRect:CGRectMake(10 + (index % 2) * (width+10), (index / 2) * (width+10), width, width) lsvId:lsvId]; - [array addObject:model]; - [dictionary setObject:lsvId forKey:lsvId]; - } - self.rectDatas = array; - self.viewsData = dictionary; - +- (void)scrollView:(LazyScrollView *)scrollView didClickItemAtIndex:(NSUInteger)index withLsvId:(NSString *)lsvId { + SingleView *view = (SingleView *)[self scrollView:scrollView itemByLsvId:lsvId]; + NSLog(@"didClickItemAtIndex:%@ lsvid:%@ view data:%@", @(index), lsvId, view.data); + [scrollView reloadData]; }