Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop: (30 commits)
  fix didEnterTimes logic
  change to common runloop modes
  fix a bug of outerScrollView feature: cannot work correctly when LazyScrollView is not outerScrollView's direct subview.
  fix some issues
  modify the ModelBucket interface a little
  Add load item views "async" feature
  modify the loadMoreData interface
  add loadMore feature and demo for it
  use model bucket to replace origin logic
  add TMLazyModelBucket and unit test for it
  fix some issues
  tidy the assemble logic
  add test project
  rename properties
  add autoClearGestures feature
  add a demo for outerScrollView
  update demo
  update outScrollView feature
  add reuse pool class
  1. tidy codes 2. remove the delegate forwarding logic and hack setContentOffset directly
  ...

# Conflicts:
#	README.md
  • Loading branch information
HarrisonXi committed Apr 20, 2018
2 parents 7073d75 + 6464418 commit faa96de
Show file tree
Hide file tree
Showing 54 changed files with 2,802 additions and 1,320 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License

Copyright (c) 2017 Alibaba
Copyright (c) 2015-2018 Alibaba

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
12 changes: 4 additions & 8 deletions LazyScroll.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@ Pod::Spec.new do |s|

s.name = "LazyScroll"
s.version = "1.0.0"
s.summary = "A ScrollView to resolve the problem of reusability of views."

s.description = <<-DESC
It reply an another way to control reuse in a ScrollView, it depends on give a special reuse identifier to every view controlled in LazyScrollView.
DESC

s.summary = "A ScrollView to resolve the problem of reusability of views."
s.homepage = "https://github.com/alibaba/LazyScrollView"
s.license = { :type => 'MIT' }
s.author = { "fydx" => "[email protected]",
"HarrisonXi" => "[email protected]" }
s.platform = :ios
s.ios.deployment_target = "5.0"
s.ios.deployment_target = "7.0"
s.source = { :git => "https://github.com/alibaba/LazyScrollView.git", :tag => "1.0.0" }
s.source_files = "LazyScrollView/*.{h,m}"
s.requires_arc = true
s.prefix_header_contents = '#import <TMUtils/TMUtils.h>'

s.dependency 'TMUtils', '~> 1.0'
s.dependency 'TMUtils', '~> 1.0.0'

end
18 changes: 18 additions & 0 deletions LazyScrollView/LazyScroll.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// LazyScroll.h
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#ifndef LazyScroll_h
#define LazyScroll_h

#import "TMLazyItemViewProtocol.h"
#import "TMLazyItemModel.h"
#import "TMLazyReusePool.h"
#import "TMLazyModelBucket.h"
#import "UIView+TMLazyScrollView.h"
#import "TMLazyScrollView.h"

#endif /* LazyScroll_h */
30 changes: 30 additions & 0 deletions LazyScrollView/TMLazyItemModel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// TMLazyItemModel.h
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

/**
It is a model to store data of item view.
*/
@interface TMLazyItemModel : NSObject

/**
Item view's frame in LazyScrollView.
*/
@property (nonatomic, assign) CGRect absRect;
@property (nonatomic, readonly) CGFloat top;
@property (nonatomic, readonly) CGFloat bottom;

/**
Item view's unique ID in LazyScrollView.
Will be set to string value of index if it's nil.
The ID MUST BE unique.
*/
@property (nonatomic, copy) NSString *muiID;

@end
22 changes: 22 additions & 0 deletions LazyScrollView/TMLazyItemModel.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// TMLazyItemModel.m
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#import "TMLazyItemModel.h"

@implementation TMLazyItemModel

- (CGFloat)top
{
return CGRectGetMinY(_absRect);
}

- (CGFloat)bottom
{
return CGRectGetMaxY(_absRect);
}

@end
42 changes: 42 additions & 0 deletions LazyScrollView/TMLazyItemViewProtocol.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// TMLazyItemViewProtocol.h
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

/**
If the item view in LazyScrollView implements this protocol, it
can receive specified event callback in LazyScrollView's lifecycle.
*/
@protocol TMLazyItemViewProtocol <NSObject>

@optional
/**
Will be called if the item view is dequeued in
'dequeueReusableItemWithIdentifier:' method.
It is similar with 'prepareForReuse' method of UITableViewCell.
*/
- (void)mui_prepareForReuse;
/**
Will be called if the item view is loaded into buffer area.
This callback always is used for setup item view.
It is similar with 'viewDidLoad' method of UIViewController.
*/
- (void)mui_afterGetView;
/**
Will be called if the item view enters the visible area.
The times starts from 0.
If the item view is in the visible area and the LazyScrollView
is reloaded, this callback will not be called.
This callback always is used for user action tracking. Sometimes,
it is also used for starting timer event.
*/
- (void)mui_didEnterWithTimes:(NSUInteger)times;
/**
Will be called if the item view leaves the visiable area.
This callback always is used for stopping timer event.
*/
- (void)mui_didLeave;

@end
31 changes: 31 additions & 0 deletions LazyScrollView/TMLazyModelBucket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// TMLazyModelBucket.h
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "TMLazyItemModel.h"

/**
Every bucket store item models in an area.
1st bucket store item models which Y value is from 0 to bucketHeight.
2nd bucket store item models which Y value is from bucketHeight to bucketHeight * 2.
*/
@interface TMLazyModelBucket : NSObject

@property (nonatomic, assign, readonly) CGFloat bucketHeight;

- (instancetype)initWithBucketHeight:(CGFloat)bucketHeight;

- (void)addModel:(TMLazyItemModel *)itemModel;
- (void)addModels:(NSSet<TMLazyItemModel *> *)itemModels;
- (void)removeModel:(TMLazyItemModel *)itemModel;
- (void)removeModels:(NSSet<TMLazyItemModel *> *)itemModels;
- (void)reloadModel:(TMLazyItemModel *)itemModel;
- (void)reloadModels:(NSSet<TMLazyItemModel *> *)itemModels;
- (void)clear;
- (NSSet<TMLazyItemModel *> *)showingModelsFrom:(CGFloat)startY to:(CGFloat)endY;

@end
111 changes: 111 additions & 0 deletions LazyScrollView/TMLazyModelBucket.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// TMLazyModelBucket.m
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#import "TMLazyModelBucket.h"

@interface TMLazyModelBucket () {
NSMutableArray<NSMutableSet *> *_buckets;
}

@end

@implementation TMLazyModelBucket

@synthesize bucketHeight = _bucketHeight;

- (instancetype)initWithBucketHeight:(CGFloat)bucketHeight
{
if (self = [super init]) {
_bucketHeight = bucketHeight;
_buckets = [NSMutableArray array];
}
return self;
}

- (void)addModel:(TMLazyItemModel *)itemModel
{
if (itemModel && itemModel.bottom > itemModel.top) {

This comment has been minimized.

Copy link
@atom2ueki

atom2ueki Apr 21, 2018

@HarrisonXi 类似这种condition itemModel.bottom > itemModel.top,是不是放在TMLazyItemModel内部比较合适呢

This comment has been minimized.

Copy link
@HarrisonXi

HarrisonXi Apr 23, 2018

Author Contributor

目前来看分桶逻辑也要用到具体的 bottom 和 top 值,当做入参校验来看的话,放在这里也可以

NSInteger startIndex = (NSInteger)floor(itemModel.top / _bucketHeight);
NSInteger endIndex = (NSInteger)floor((itemModel.bottom - 0.01) / _bucketHeight);
for (NSInteger index = 0; index <= endIndex; index++) {
if (_buckets.count <= index) {
[_buckets addObject:[NSMutableSet set]];
}
if (index >= startIndex && index <= endIndex) {
NSMutableSet *bucket = [_buckets objectAtIndex:index];
[bucket addObject:itemModel];
}
}
}
}

- (void)addModels:(NSSet<TMLazyItemModel *> *)itemModels
{
if (itemModels) {
for (TMLazyItemModel *itemModel in itemModels) {
[self addModel:itemModel];
}
}
}

- (void)removeModel:(TMLazyItemModel *)itemModel
{
if (itemModel) {
for (NSMutableSet *bucket in _buckets) {
[bucket removeObject:itemModel];
}
}
}

- (void)removeModels:(NSSet<TMLazyItemModel *> *)itemModels
{
if (itemModels) {
for (NSMutableSet *bucket in _buckets) {
[bucket minusSet:itemModels];
}
}
}

- (void)reloadModel:(TMLazyItemModel *)itemModel
{
[self removeModel:itemModel];
[self addModel:itemModel];
}

- (void)reloadModels:(NSSet<TMLazyItemModel *> *)itemModels
{
[self removeModels:itemModels];
[self addModels:itemModels];
}

- (void)clear
{
[_buckets removeAllObjects];
}

- (NSSet<TMLazyItemModel *> *)showingModelsFrom:(CGFloat)startY to:(CGFloat)endY
{
NSMutableSet *result = [NSMutableSet set];
NSInteger startIndex = (NSInteger)floor(startY / _bucketHeight);
NSInteger endIndex = (NSInteger)floor((endY - 0.01) / _bucketHeight);
for (NSInteger index = 0; index <= endIndex; index++) {
if (_buckets.count > index && index >= startIndex && index <= endIndex) {
NSSet *bucket = [_buckets objectAtIndex:index];
[result unionSet:bucket];
}
}
NSMutableSet *needToBeRemoved = [NSMutableSet set];
for (TMLazyItemModel *itemModel in result) {
if (itemModel.top >= endY || itemModel.bottom <= startY) {
[needToBeRemoved addObject:itemModel];
}
}
[result minusSet:needToBeRemoved];
return [result copy];
}

@end
18 changes: 18 additions & 0 deletions LazyScrollView/TMLazyReusePool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// TMLazyReusePool.h
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface TMLazyReusePool : NSObject

- (void)addItemView:(UIView *)itemView forReuseIdentifier:(NSString *)reuseIdentifier;
- (UIView *)dequeueItemViewForReuseIdentifier:(NSString *)reuseIdentifier;
- (UIView *)dequeueItemViewForReuseIdentifier:(NSString *)reuseIdentifier andMuiID:(NSString *)muiID;
- (void)clear;
- (NSSet<UIView *> *)allItemViews;

@end
85 changes: 85 additions & 0 deletions LazyScrollView/TMLazyReusePool.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// TMLazyReusePool.m
// LazyScrollView
//
// Copyright (c) 2015-2018 Alibaba. All rights reserved.
//

#import "TMLazyReusePool.h"
#import "UIView+TMLazyScrollView.h"

@interface TMLazyReusePool () {
NSMutableDictionary<NSString *, NSMutableSet *> *_reuseDict;
}

@end

@implementation TMLazyReusePool

- (instancetype)init
{
if (self = [super init]) {
_reuseDict = [NSMutableDictionary dictionary];
}
return self;
}

- (void)addItemView:(UIView *)itemView forReuseIdentifier:(NSString *)reuseIdentifier
{
if (reuseIdentifier == nil || reuseIdentifier.length == 0 || itemView == nil) {
return;
}
NSMutableSet *reuseSet = [_reuseDict tm_safeObjectForKey:reuseIdentifier];
if (!reuseSet) {
reuseSet = [NSMutableSet set];
[_reuseDict setObject:reuseSet forKey:reuseIdentifier];
}
[reuseSet addObject:itemView];
}

- (UIView *)dequeueItemViewForReuseIdentifier:(NSString *)reuseIdentifier
{
return [self dequeueItemViewForReuseIdentifier:reuseIdentifier andMuiID:nil];
}

- (UIView *)dequeueItemViewForReuseIdentifier:(NSString *)reuseIdentifier andMuiID:(NSString *)muiID
{
if (reuseIdentifier == nil || reuseIdentifier.length == 0) {
return nil;
}
UIView *result = nil;
NSMutableSet *reuseSet = [_reuseDict tm_safeObjectForKey:reuseIdentifier];
if (reuseSet && reuseSet.count > 0) {
if (!muiID) {
result = [reuseSet anyObject];
} else {
for (UIView *itemView in reuseSet) {
if ([itemView.muiID isEqualToString:muiID]) {
result = itemView;
break;
}
}
if (!result) {
result = [reuseSet anyObject];
}
}
[reuseSet removeObject:result];
}
return result;
}

- (void)clear
{
[_reuseDict removeAllObjects];
}

- (NSSet<UIView *> *)allItemViews
{
NSMutableSet *result = [NSMutableSet set];
for (NSMutableSet *reuseSet in _reuseDict.allValues) {
[result unionSet:reuseSet];
}
return [result copy];
}

@end
Loading

0 comments on commit faa96de

Please sign in to comment.