├── .gitignore ├── README.md ├── WKPagesCollectionView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── reynoldqin.xcuserdatad │ └── xcschemes │ ├── WKPagesScrollView.xcscheme │ └── xcschememanagement.plist └── WKPagesScrollView ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj └── Main.storyboard ├── Images.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── LaunchImage.launchimage │ └── Contents.json ├── image-0.imageset │ ├── Contents.json │ ├── weather-default-bg.png │ └── weather-default-bg@2x.png ├── image-1.imageset │ ├── Contents.json │ └── IMG_1107.png └── image-2.imageset │ ├── Contents.json │ └── IMG_1364.png ├── ViewController.h ├── ViewController.m ├── WKPagesCollectionView-Info.plist ├── WKPagesCollectionView-Prefix.pch ├── WKPagesCollectionView ├── WK.h ├── WKPagesCollectionView.h ├── WKPagesCollectionView.m ├── WKPagesCollectionViewCell.h ├── WKPagesCollectionViewCell.m ├── WKPagesCollectionViewFlowLayout.h └── WKPagesCollectionViewFlowLayout.m ├── en.lproj └── InfoPlist.strings └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | .DS_Store 17 | output/* 18 | *.swp 19 | *.pyc 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WKPagesCollectionView 2 | 3 | 我尝试想做一个类似 iOS7 下的 safari tabs 页面那样的效果。 4 | 5 | * 有页面翻转的效果; 6 | * 点击一个页面变成正常的显示状态; 7 | * 往左划动会删除这个cell; 8 | * 可以在底部添加新的cell; 9 | 10 | [点击查看效果视频](http://v.youku.com/v_show/id_XNzAzNDg4OTQ4.html) 11 | 12 | ![效果视频](http://farm4.staticflickr.com/3829/11171831814_9c5972bbe6_z.jpg)] 13 | 14 | 15 | 16 | ##使用 17 | * 项目中添加 WKPagesCollectionView目录以及下面的`WK.h`, `WKPagesCollectionView.h`, `WKPagesCollectionView.m`, `WKPagesCollectionViewCell.h`, `WKPagesCollectionViewCell.m`, `WKPagesCollectionViewFlowLayout.h`, `WKPagesCollectionViewFlowLayout.m`; 18 | * 引用 `WKPagesCollectionView`; 19 | * 准备数据 20 | 21 | _array=[[NSMutableArray alloc]init]; 22 | for (int a=0; a<=30; a++) { 23 | [_array addObject:[NSString stringWithFormat:@"button %d",a]]; 24 | } 25 | 26 | * 创建collectionView 27 | 28 | _collectionView=[[[WKPagesCollectionView alloc]initWithPagesFlowLayoutAndFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.view.frame.size.height)] autorelease]; 29 | _collectionView.dataSource=self; 30 | _collectionView.delegate=self; 31 | [_collectionView registerClass:[WKPagesCollectionViewCell class] forCellWithReuseIdentifier:@"cell"]; 32 | [self.view addSubview:_collectionView]; 33 | _collectionView.maskShow=YES; 34 | 35 | 36 | * 完成WKPagesCollectionViewDataSource(继承自UICollectionViewDataSource) 和 WKPagesCollectionViewDelegate(继承自UICollectionViewDelegate), 除了要完成UICollectionViewDataSource中提供数据的方法外,还要完成追加数据的方法`-(void)willAppendItemInCollectionView:(WKPagesCollectionView *)collectionView` 37 | 和删除数据的方法` 38 | -(void)collectionView:(WKPagesCollectionView *)collectionView willRemoveCellAtNSIndexPath:(NSIndexPath *)indexPath` 39 | 40 | #pragma mark - UICollectionViewDataSource and UICollectionViewDelegate 41 | -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 42 | return _array.count; 43 | } 44 | -(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 45 | // NSLog(@"cellForItemAtIndexPath:%d",indexPath.row); 46 | static NSString* identity=@"cell"; 47 | WKPagesCollectionViewCell* cell=(WKPagesCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:identity forIndexPath:indexPath]; 48 | cell.collectionView=collectionView; 49 | UIImageView* imageView=[[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"image-0"]] autorelease]; 50 | imageView.frame=self.view.bounds; 51 | [cell.cellContentView addSubview:imageView]; 52 | UIButton* button=[UIButton buttonWithType:UIButtonTypeCustom]; 53 | button.frame=CGRectMake(0, (indexPath.row+1)*10+100, 320, 50.0f); 54 | button.backgroundColor=[UIColor whiteColor]; 55 | [button setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 56 | [button setTitle:_array[indexPath.row] forState:UIControlStateNormal]; 57 | [button addTarget:self action:@selector(onButtonTitle:) forControlEvents:UIControlEventTouchUpInside]; 58 | [cell.cellContentView addSubview:button]; 59 | return cell; 60 | } 61 | #pragma mark WKPagesCollectionViewDataSource 62 | ///追加数据 63 | -(void)willAppendItemInCollectionView:(WKPagesCollectionView *)collectionView{ 64 | [_array addObject:@"new button"]; 65 | } 66 | ///删除数据 67 | -(void)collectionView:(WKPagesCollectionView *)collectionView willRemoveCellAtNSIndexPath:(NSIndexPath *)indexPath{ 68 | [_array removeObjectAtIndex:indexPath.row]; 69 | } 70 | 71 | * 添加一个页面时的操作 72 | 73 | -(IBAction)onButtonAdd:(id)sender{ 74 | [_collectionView appendItem]; 75 | } 76 | 77 | * 展开显示一个具体的页面,这个在点击页面的时候会自动实现,也可以像下面这样代码调用; 78 | 79 | [_collectionView showCellToHighLightAtIndexPath:indexPath completion:^(BOOL finished) { 80 | NSLog(@"highlight completed"); 81 | }]; 82 | 83 | * 停止展开,回到普通的滚动模式 84 | 85 | -(IBAction)onButtonTitle:(id)sender{ 86 | NSLog(@"button"); 87 | [_collectionView dismissFromHightLightWithCompletion:^(BOOL finished) { 88 | NSLog(@"dismiss completed"); 89 | }]; 90 | } 91 | 92 | ##TODO 93 | * `bug` ~~每滚动几个时候顶上的那一个就会先看不见然后又突然出现了,还没想到原因~~ 这个问题已经解决,@Nikolay Abelyashev 修复了这个bug,原因是由于WKPagesCollectionView的高度太小,而UICollectionView会把屏幕外的cell不在显示,在WKPagesCollectionView中,屏幕外的3个cell会被取消显示,解决的办法是修改WKPagesCollectionView的frame,使他高出window(这里添加了 topOfScreen:120.0f),然后把frame.origin.y也往上移动了那么多距离,这样WKPagesCollectionView其实就更大; 94 | 95 | 96 | 97 | ##实现的方式 98 | ###实现滚动 99 | 100 | 我使用了UICollectionView来实现,本质上是一个垂直的列表,而主要的工作是来创造一个CollectionViewLayout, (我定义为WKPagesCollectionViewFlowLayout)每一个cell其实是和当前屏幕一样大小的,也就是说其实就是有一堆和屏幕一样大的cell错开折叠在一起,他们之间的间隔设置为`self.minimumLineSpacing=-1*(self.itemSize.height-160.0f);`。 101 | ![不翻转cell时](http://farm6.staticflickr.com/5521/11171968153_7a7aeb5893_z.jpg) 102 | 103 | 为了实现翻转的效果,我在WKPagesCollectionViewFlowLayout中的 104 | `-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect` 中修改了transform3D。 105 | 106 | 看上去貌似比较简单,而如果所有的页面都是固定的角度的话,的确也没问题,但是我想像safari里那样在滚动时有点视差的效果,所以就为每个页面设置了不同的翻转角度了,其实就是在layoutAttributesForElementsInRect 中根据每个cell的位置来计算角度使得他们在滚动条滚动时有点不同的角度,下面这个是设置角度的方法: 107 | 108 | 109 | -(void)makeRotateTransformForAttributes:(UICollectionViewLayoutAttributes*)attributes{ 110 | attributes.zIndex=attributes.indexPath.row;///要设置zIndex,否则遮挡顺序会有编号 111 | CGFloat distance=attributes.frame.origin.y-self.collectionView.contentOffset.y; 112 | CGFloat normalizedDistance = distance / self.collectionView.frame.size.height; 113 | normalizedDistance=fmaxf(normalizedDistance, 0.0f); 114 | CGFloat rotate=RotateDegree+20.0f*normalizedDistance; 115 | //CGFloat rotate=RotateDegree; 116 | NSLog(@"makeRotateTransformForAttributes:row:%d,normalizedDistance:%f,rotate:%f", 117 | attributes.indexPath.row,normalizedDistance,rotate); 118 | ///角度大的会和角度小的cell交叉,即使设置zIndex也没有用,这里设置底部的cell角度越来越大 119 | CATransform3D rotateTransform=WKFlipCATransform3DPerspectSimpleWithRotate(rotate); 120 | attributes.transform3D=rotateTransform; 121 | 122 | } 123 | 124 | ``` 125 | -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path 126 | { 127 | NSLog(@"layoutAttributesForItemAtIndexPath:%d",path.row); 128 | UICollectionViewLayoutAttributes* attributes=[super layoutAttributesForItemAtIndexPath:path]; 129 | [self makeRotateTransformForAttributes:attributes]; 130 | return attributes; 131 | } 132 | -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect 133 | { 134 | NSLog(@"layoutAttributesForElementsInRect:%@",NSStringFromCGRect(rect)); 135 | NSArray* array = [super layoutAttributesForElementsInRect:rect]; 136 | for (UICollectionViewLayoutAttributes* attributes in array) { 137 | [self makeRotateTransformForAttributes:attributes]; 138 | } 139 | return array; 140 | } 141 | ``` 142 | 143 | 现在运行的时候,滚动起来就和想要的效果差不多了,虽然不如safari那么细节完美,大概的意思是达到了。 144 | 145 | ##实现删除 146 | 147 | 后面我想做出safari那样按住其中一个cell往左滑动就删除的效果,在每个cell里面添加一个scrollView,然后scrollViewDidEndDragging 中达到一定距离的时候就触发删除好了,而UICollectionView中的performBatchUpdates就可以很好的完成删除的动画了。 148 | 149 | ![删除时的效果](http://farm4.staticflickr.com/3831/11171811316_c681d80cc2_z.jpg) 150 | 151 | -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ 152 | if (self.showingState==WKPagesCollectionViewCellShowingStateNormal){ 153 | if (scrollView.contentOffset.x>=90.0f){ 154 | NSIndexPath* indexPath=[self.collectionView indexPathForCell:self]; 155 | NSLog(@"delete cell at %d",indexPath.row); 156 | //self.alpha=0.0f; 157 | ///删除数据 158 | id pagesDataSource=(id)self.collectionView.dataSource; 159 | [pagesDataSource collectionView:(WKPagesCollectionView*)self.collectionView willRemoveCellAtNSIndexPath:indexPath]; 160 | ///动画 161 | [self.collectionView performBatchUpdates:^{ 162 | [self.collectionView deleteItemsAtIndexPaths:@[indexPath,]]; 163 | } completion:^(BOOL finished) { 164 | 165 | }]; 166 | } 167 | } 168 | } 169 | 170 | 为了添加和删除的时候动画的好看一点,我们还得修改 171 | `(UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath` 和 `-(UICollectionViewLayoutAttributes*)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath` 172 | 173 | 我在WKPagesCollectionViewFlowLayout 中添加了insertIndexPaths 和 deleteIndexPaths 来记录用来添加和删除的位置,因为这个两个回调在添加和删除时会被调用,而且不仅仅是针对正在添加或者删除的NSIndexPath,其他行也会被调用,而我们这里只要处理正在添加和删除的NSIndexPath; 174 | 175 | -(UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ 176 | UICollectionViewLayoutAttributes* attributes=[super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; 177 | NSLog(@"initialLayoutAttributesForAppearingItemAtIndexPath:%d",itemIndexPath.row); 178 | if ([self.insertIndexPaths containsObject:itemIndexPath]){ 179 | if (!attributes) 180 | attributes=[self layoutAttributesForItemAtIndexPath:itemIndexPath]; 181 | CATransform3D rotateTransform=WKFlipCATransform3DPerspectSimpleWithRotate(-90.0f); 182 | attributes.transform3D=rotateTransform; 183 | } 184 | return attributes; 185 | } 186 | -(UICollectionViewLayoutAttributes*)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ 187 | NSLog(@"finalLayoutAttributesForDisappearingItemAtIndexPath:%d",itemIndexPath.row); 188 | UICollectionViewLayoutAttributes* attributes=[super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath]; 189 | if ([self.deleteIndexPaths containsObject:itemIndexPath]){ 190 | if (!attributes){ 191 | attributes=[self layoutAttributesForItemAtIndexPath:itemIndexPath]; 192 | } 193 | CATransform3D moveTransform=CATransform3DMakeTranslation(-320.0f, 0.0f, 0.0f); 194 | attributes.transform3D=CATransform3DConcat(attributes.transform3D, moveTransform); 195 | } 196 | return attributes; 197 | 198 | } 199 | 200 | 201 | 开始的时候会发生一些意想不到的动画效果,滚动到底部,然后在模拟器下按下command+t打开慢速动画的时候就看的很清楚了,往左滑动来删除最后两个cell,在删除时一开始的效果是正常的,而几乎在完成之后,会看到一闪,出现了两个cell在很奇怪的位置,然后又动画慢慢回到预订的位置。 202 | 203 | 现在的问题就是,如果我删除带有button-0,button-1,button-2,button-3这样的cell,动画是正常的,但是如果我删除最后几个cell,带有button-6,button-7,button-8的,就会出现意想不到的动画。 204 | 205 | 而且我发现如果我的cell的翻转角度如果是全部固定的,那在删除cell时是不会发生奇怪的动画的,我的makeRotateTransformForAttributes2中是指定了一个固定的角度。 206 | 207 | ####Fixed 208 | 209 | 后来终于知道问题在哪里了,只是由于contentSize计算错误导致内容区域不够所以在删除cell后又自动滚动时产生了奇怪的行为,所以`-(CGSize)collectionViewContentSize`中的高度一定要正确。 210 | 211 | ###实现添加页面 212 | 213 | 现在页面有两种状态,一种就是这种普通的滚动页面的状态,当点击某一个页面的时候,他会翻转到全屏显示,这时我称作是`highlight`。如果只是在普通的滚动状态下,会先滚动到屏幕地步,然后添加一个页面,之后又会把这个页面展开到highLight显示状态。而如果现在整个collectionView本身就已经有一个页面在hightLight了,那应该先退回到普通状态,再重复之前的添加页面过程。 214 | 215 | 下面是在WKPagesCollectionView中的添加页面的方法; 216 | 217 | ///追加一个页面 218 | -(void)appendItem{ 219 | if (self.isHighLight){ 220 | [self dismissFromHightLightWithCompletion:^(BOOL finished) { 221 | [self _addNewPage]; 222 | }]; 223 | } 224 | else{ 225 | [self _addNewPage]; 226 | } 227 | } 228 | ///添加一页 229 | -(void)_addNewPage{ 230 | int total=[self numberOfItemsInSection:0]; 231 | [self scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:total-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:YES]; 232 | double delayInSeconds = 0.3f; 233 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 234 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 235 | ///添加数据 236 | [(id)self.dataSource willAppendItemInCollectionView:self]; 237 | int lastRow=total; 238 | NSIndexPath* insertIndexPath=[NSIndexPath indexPathForItem:lastRow inSection:0]; 239 | [self performBatchUpdates:^{ 240 | [self insertItemsAtIndexPaths:@[insertIndexPath]]; 241 | } completion:^(BOOL finished) { 242 | double delayInSeconds = 0.3f; 243 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 244 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 245 | [self showCellToHighLightAtIndexPath:insertIndexPath completion:^(BOOL finished) { 246 | 247 | }]; 248 | }); 249 | 250 | }]; 251 | }); 252 | } 253 | -------------------------------------------------------------------------------- /WKPagesCollectionView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 68A1CF27184CD0DB00346DF8 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 68A1CF26184CD0DB00346DF8 /* README.md */; }; 11 | 68BDCE7C18432CCA00DB81A2 /* WKPagesCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCE7718432CCA00DB81A2 /* WKPagesCollectionView.m */; }; 12 | 68BDCE7D18432CCA00DB81A2 /* WKPagesCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCE7918432CCA00DB81A2 /* WKPagesCollectionViewCell.m */; }; 13 | 68BDCE7E18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCE7B18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.m */; }; 14 | 68D7C1D318347A08006B418C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C1D218347A08006B418C /* Foundation.framework */; }; 15 | 68D7C1D518347A08006B418C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C1D418347A08006B418C /* CoreGraphics.framework */; }; 16 | 68D7C1D718347A08006B418C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C1D618347A08006B418C /* UIKit.framework */; }; 17 | 68D7C1DD18347A08006B418C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 68D7C1DB18347A08006B418C /* InfoPlist.strings */; }; 18 | 68D7C1DF18347A08006B418C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D7C1DE18347A08006B418C /* main.m */; }; 19 | 68D7C1E318347A08006B418C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D7C1E218347A08006B418C /* AppDelegate.m */; }; 20 | 68D7C1E618347A08006B418C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 68D7C1E418347A08006B418C /* Main.storyboard */; }; 21 | 68D7C1E918347A08006B418C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D7C1E818347A08006B418C /* ViewController.m */; }; 22 | 68D7C1EB18347A08006B418C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 68D7C1EA18347A08006B418C /* Images.xcassets */; }; 23 | 68D7C1F218347A08006B418C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C1F118347A08006B418C /* XCTest.framework */; }; 24 | 68D7C1F318347A08006B418C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C1D218347A08006B418C /* Foundation.framework */; }; 25 | 68D7C1F418347A08006B418C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C1D618347A08006B418C /* UIKit.framework */; }; 26 | 68D7C20E18349B4C006B418C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D7C20D18349B4C006B418C /* QuartzCore.framework */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 68D7C1F518347A08006B418C /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 68D7C1C718347A08006B418C /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 68D7C1CE18347A08006B418C; 35 | remoteInfo = WKPagesScrollView; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 68A1CF26184CD0DB00346DF8 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = SOURCE_ROOT; }; 41 | 68BDCE7518432CCA00DB81A2 /* WK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WK.h; sourceTree = ""; }; 42 | 68BDCE7618432CCA00DB81A2 /* WKPagesCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKPagesCollectionView.h; sourceTree = ""; }; 43 | 68BDCE7718432CCA00DB81A2 /* WKPagesCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKPagesCollectionView.m; sourceTree = ""; }; 44 | 68BDCE7818432CCA00DB81A2 /* WKPagesCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKPagesCollectionViewCell.h; sourceTree = ""; }; 45 | 68BDCE7918432CCA00DB81A2 /* WKPagesCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKPagesCollectionViewCell.m; sourceTree = ""; }; 46 | 68BDCE7A18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKPagesCollectionViewFlowLayout.h; sourceTree = ""; }; 47 | 68BDCE7B18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKPagesCollectionViewFlowLayout.m; sourceTree = ""; }; 48 | 68D7C1CF18347A08006B418C /* WKPagesCollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WKPagesCollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 68D7C1D218347A08006B418C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 50 | 68D7C1D418347A08006B418C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 51 | 68D7C1D618347A08006B418C /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 52 | 68D7C1DA18347A08006B418C /* WKPagesCollectionView-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "WKPagesCollectionView-Info.plist"; sourceTree = ""; }; 53 | 68D7C1DC18347A08006B418C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 54 | 68D7C1DE18347A08006B418C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 68D7C1E018347A08006B418C /* WKPagesCollectionView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WKPagesCollectionView-Prefix.pch"; sourceTree = ""; }; 56 | 68D7C1E118347A08006B418C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 57 | 68D7C1E218347A08006B418C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 58 | 68D7C1E518347A08006B418C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 59 | 68D7C1E718347A08006B418C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 60 | 68D7C1E818347A08006B418C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 61 | 68D7C1EA18347A08006B418C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 62 | 68D7C1F018347A08006B418C /* WKPagesCollectionViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WKPagesCollectionViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 68D7C1F118347A08006B418C /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 64 | 68D7C20D18349B4C006B418C /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 68D7C1CC18347A08006B418C /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | 68D7C20E18349B4C006B418C /* QuartzCore.framework in Frameworks */, 73 | 68D7C1D518347A08006B418C /* CoreGraphics.framework in Frameworks */, 74 | 68D7C1D718347A08006B418C /* UIKit.framework in Frameworks */, 75 | 68D7C1D318347A08006B418C /* Foundation.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 68D7C1ED18347A08006B418C /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | 68D7C1F218347A08006B418C /* XCTest.framework in Frameworks */, 84 | 68D7C1F418347A08006B418C /* UIKit.framework in Frameworks */, 85 | 68D7C1F318347A08006B418C /* Foundation.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | /* End PBXFrameworksBuildPhase section */ 90 | 91 | /* Begin PBXGroup section */ 92 | 68BDCE7418432CCA00DB81A2 /* WKPagesCollectionView */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 68BDCE7518432CCA00DB81A2 /* WK.h */, 96 | 68BDCE7618432CCA00DB81A2 /* WKPagesCollectionView.h */, 97 | 68BDCE7718432CCA00DB81A2 /* WKPagesCollectionView.m */, 98 | 68BDCE7818432CCA00DB81A2 /* WKPagesCollectionViewCell.h */, 99 | 68BDCE7918432CCA00DB81A2 /* WKPagesCollectionViewCell.m */, 100 | 68BDCE7A18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.h */, 101 | 68BDCE7B18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.m */, 102 | ); 103 | path = WKPagesCollectionView; 104 | sourceTree = ""; 105 | }; 106 | 68D7C1C618347A08006B418C = { 107 | isa = PBXGroup; 108 | children = ( 109 | 68D7C1D818347A08006B418C /* WKPagesCollectionView */, 110 | 68D7C1D118347A08006B418C /* Frameworks */, 111 | 68D7C1D018347A08006B418C /* Products */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | 68D7C1D018347A08006B418C /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 68D7C1CF18347A08006B418C /* WKPagesCollectionView.app */, 119 | 68D7C1F018347A08006B418C /* WKPagesCollectionViewTests.xctest */, 120 | ); 121 | name = Products; 122 | sourceTree = ""; 123 | }; 124 | 68D7C1D118347A08006B418C /* Frameworks */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 68D7C20D18349B4C006B418C /* QuartzCore.framework */, 128 | 68D7C1D218347A08006B418C /* Foundation.framework */, 129 | 68D7C1D418347A08006B418C /* CoreGraphics.framework */, 130 | 68D7C1D618347A08006B418C /* UIKit.framework */, 131 | 68D7C1F118347A08006B418C /* XCTest.framework */, 132 | ); 133 | name = Frameworks; 134 | sourceTree = ""; 135 | }; 136 | 68D7C1D818347A08006B418C /* WKPagesCollectionView */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 68A1CF26184CD0DB00346DF8 /* README.md */, 140 | 68D7C1E118347A08006B418C /* AppDelegate.h */, 141 | 68D7C1E218347A08006B418C /* AppDelegate.m */, 142 | 68D7C1E418347A08006B418C /* Main.storyboard */, 143 | 68D7C1E718347A08006B418C /* ViewController.h */, 144 | 68D7C1E818347A08006B418C /* ViewController.m */, 145 | 68D7C1EA18347A08006B418C /* Images.xcassets */, 146 | 68D7C1D918347A08006B418C /* Supporting Files */, 147 | 68BDCE7418432CCA00DB81A2 /* WKPagesCollectionView */, 148 | ); 149 | name = WKPagesCollectionView; 150 | path = WKPagesScrollView; 151 | sourceTree = ""; 152 | }; 153 | 68D7C1D918347A08006B418C /* Supporting Files */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 68D7C1DA18347A08006B418C /* WKPagesCollectionView-Info.plist */, 157 | 68D7C1DB18347A08006B418C /* InfoPlist.strings */, 158 | 68D7C1DE18347A08006B418C /* main.m */, 159 | 68D7C1E018347A08006B418C /* WKPagesCollectionView-Prefix.pch */, 160 | ); 161 | name = "Supporting Files"; 162 | sourceTree = ""; 163 | }; 164 | /* End PBXGroup section */ 165 | 166 | /* Begin PBXNativeTarget section */ 167 | 68D7C1CE18347A08006B418C /* WKPagesCollectionView */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 68D7C20118347A08006B418C /* Build configuration list for PBXNativeTarget "WKPagesCollectionView" */; 170 | buildPhases = ( 171 | 68D7C1CB18347A08006B418C /* Sources */, 172 | 68D7C1CC18347A08006B418C /* Frameworks */, 173 | 68D7C1CD18347A08006B418C /* Resources */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | ); 179 | name = WKPagesCollectionView; 180 | productName = WKPagesScrollView; 181 | productReference = 68D7C1CF18347A08006B418C /* WKPagesCollectionView.app */; 182 | productType = "com.apple.product-type.application"; 183 | }; 184 | 68D7C1EF18347A08006B418C /* WKPagesCollectionViewTests */ = { 185 | isa = PBXNativeTarget; 186 | buildConfigurationList = 68D7C20418347A08006B418C /* Build configuration list for PBXNativeTarget "WKPagesCollectionViewTests" */; 187 | buildPhases = ( 188 | 68D7C1EC18347A08006B418C /* Sources */, 189 | 68D7C1ED18347A08006B418C /* Frameworks */, 190 | 68D7C1EE18347A08006B418C /* Resources */, 191 | ); 192 | buildRules = ( 193 | ); 194 | dependencies = ( 195 | 68D7C1F618347A08006B418C /* PBXTargetDependency */, 196 | ); 197 | name = WKPagesCollectionViewTests; 198 | productName = WKPagesScrollViewTests; 199 | productReference = 68D7C1F018347A08006B418C /* WKPagesCollectionViewTests.xctest */; 200 | productType = "com.apple.product-type.bundle.unit-test"; 201 | }; 202 | /* End PBXNativeTarget section */ 203 | 204 | /* Begin PBXProject section */ 205 | 68D7C1C718347A08006B418C /* Project object */ = { 206 | isa = PBXProject; 207 | attributes = { 208 | LastUpgradeCheck = 0500; 209 | ORGANIZATIONNAME = "秦 道平"; 210 | TargetAttributes = { 211 | 68D7C1EF18347A08006B418C = { 212 | TestTargetID = 68D7C1CE18347A08006B418C; 213 | }; 214 | }; 215 | }; 216 | buildConfigurationList = 68D7C1CA18347A08006B418C /* Build configuration list for PBXProject "WKPagesCollectionView" */; 217 | compatibilityVersion = "Xcode 3.2"; 218 | developmentRegion = English; 219 | hasScannedForEncodings = 0; 220 | knownRegions = ( 221 | en, 222 | Base, 223 | ); 224 | mainGroup = 68D7C1C618347A08006B418C; 225 | productRefGroup = 68D7C1D018347A08006B418C /* Products */; 226 | projectDirPath = ""; 227 | projectRoot = ""; 228 | targets = ( 229 | 68D7C1CE18347A08006B418C /* WKPagesCollectionView */, 230 | 68D7C1EF18347A08006B418C /* WKPagesCollectionViewTests */, 231 | ); 232 | }; 233 | /* End PBXProject section */ 234 | 235 | /* Begin PBXResourcesBuildPhase section */ 236 | 68D7C1CD18347A08006B418C /* Resources */ = { 237 | isa = PBXResourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | 68D7C1EB18347A08006B418C /* Images.xcassets in Resources */, 241 | 68D7C1DD18347A08006B418C /* InfoPlist.strings in Resources */, 242 | 68A1CF27184CD0DB00346DF8 /* README.md in Resources */, 243 | 68D7C1E618347A08006B418C /* Main.storyboard in Resources */, 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | 68D7C1EE18347A08006B418C /* Resources */ = { 248 | isa = PBXResourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | /* End PBXResourcesBuildPhase section */ 255 | 256 | /* Begin PBXSourcesBuildPhase section */ 257 | 68D7C1CB18347A08006B418C /* Sources */ = { 258 | isa = PBXSourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | 68D7C1E918347A08006B418C /* ViewController.m in Sources */, 262 | 68D7C1E318347A08006B418C /* AppDelegate.m in Sources */, 263 | 68BDCE7C18432CCA00DB81A2 /* WKPagesCollectionView.m in Sources */, 264 | 68D7C1DF18347A08006B418C /* main.m in Sources */, 265 | 68BDCE7E18432CCA00DB81A2 /* WKPagesCollectionViewFlowLayout.m in Sources */, 266 | 68BDCE7D18432CCA00DB81A2 /* WKPagesCollectionViewCell.m in Sources */, 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | 68D7C1EC18347A08006B418C /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | /* End PBXSourcesBuildPhase section */ 278 | 279 | /* Begin PBXTargetDependency section */ 280 | 68D7C1F618347A08006B418C /* PBXTargetDependency */ = { 281 | isa = PBXTargetDependency; 282 | target = 68D7C1CE18347A08006B418C /* WKPagesCollectionView */; 283 | targetProxy = 68D7C1F518347A08006B418C /* PBXContainerItemProxy */; 284 | }; 285 | /* End PBXTargetDependency section */ 286 | 287 | /* Begin PBXVariantGroup section */ 288 | 68D7C1DB18347A08006B418C /* InfoPlist.strings */ = { 289 | isa = PBXVariantGroup; 290 | children = ( 291 | 68D7C1DC18347A08006B418C /* en */, 292 | ); 293 | name = InfoPlist.strings; 294 | sourceTree = ""; 295 | }; 296 | 68D7C1E418347A08006B418C /* Main.storyboard */ = { 297 | isa = PBXVariantGroup; 298 | children = ( 299 | 68D7C1E518347A08006B418C /* Base */, 300 | ); 301 | name = Main.storyboard; 302 | sourceTree = ""; 303 | }; 304 | /* End PBXVariantGroup section */ 305 | 306 | /* Begin XCBuildConfiguration section */ 307 | 68D7C1FF18347A08006B418C /* Debug */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ALWAYS_SEARCH_USER_PATHS = NO; 311 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 323 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 324 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 325 | COPY_PHASE_STRIP = NO; 326 | GCC_C_LANGUAGE_STANDARD = gnu99; 327 | GCC_DYNAMIC_NO_PIC = NO; 328 | GCC_OPTIMIZATION_LEVEL = 0; 329 | GCC_PREPROCESSOR_DEFINITIONS = ( 330 | "DEBUG=1", 331 | "$(inherited)", 332 | ); 333 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 341 | ONLY_ACTIVE_ARCH = YES; 342 | SDKROOT = iphoneos; 343 | }; 344 | name = Debug; 345 | }; 346 | 68D7C20018347A08006B418C /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 352 | CLANG_CXX_LIBRARY = "libc++"; 353 | CLANG_ENABLE_MODULES = YES; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_WARN_BOOL_CONVERSION = YES; 356 | CLANG_WARN_CONSTANT_CONVERSION = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_EMPTY_BODY = YES; 359 | CLANG_WARN_ENUM_CONVERSION = YES; 360 | CLANG_WARN_INT_CONVERSION = YES; 361 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 364 | COPY_PHASE_STRIP = YES; 365 | ENABLE_NS_ASSERTIONS = NO; 366 | GCC_C_LANGUAGE_STANDARD = gnu99; 367 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 368 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 369 | GCC_WARN_UNDECLARED_SELECTOR = YES; 370 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 371 | GCC_WARN_UNUSED_FUNCTION = YES; 372 | GCC_WARN_UNUSED_VARIABLE = YES; 373 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 374 | SDKROOT = iphoneos; 375 | VALIDATE_PRODUCT = YES; 376 | }; 377 | name = Release; 378 | }; 379 | 68D7C20218347A08006B418C /* Debug */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 383 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 384 | CLANG_ENABLE_OBJC_ARC = NO; 385 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 386 | GCC_PREFIX_HEADER = "WKPagesScrollView/WKPagesCollectionView-Prefix.pch"; 387 | INFOPLIST_FILE = "WKPagesScrollView/WKPagesCollectionView-Info.plist"; 388 | PRODUCT_NAME = WKPagesCollectionView; 389 | WRAPPER_EXTENSION = app; 390 | }; 391 | name = Debug; 392 | }; 393 | 68D7C20318347A08006B418C /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 397 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 398 | CLANG_ENABLE_OBJC_ARC = NO; 399 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 400 | GCC_PREFIX_HEADER = "WKPagesScrollView/WKPagesCollectionView-Prefix.pch"; 401 | INFOPLIST_FILE = "WKPagesScrollView/WKPagesCollectionView-Info.plist"; 402 | PRODUCT_NAME = WKPagesCollectionView; 403 | WRAPPER_EXTENSION = app; 404 | }; 405 | name = Release; 406 | }; 407 | 68D7C20518347A08006B418C /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 411 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WKPagesScrollView.app/WKPagesScrollView"; 412 | FRAMEWORK_SEARCH_PATHS = ( 413 | "$(SDKROOT)/Developer/Library/Frameworks", 414 | "$(inherited)", 415 | "$(DEVELOPER_FRAMEWORKS_DIR)", 416 | ); 417 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 418 | GCC_PREFIX_HEADER = "WKPagesScrollView/WKPagesScrollView-Prefix.pch"; 419 | GCC_PREPROCESSOR_DEFINITIONS = ( 420 | "DEBUG=1", 421 | "$(inherited)", 422 | ); 423 | INFOPLIST_FILE = "WKPagesScrollViewTests/WKPagesScrollViewTests-Info.plist"; 424 | PRODUCT_NAME = WKPagesCollectionViewTests; 425 | TEST_HOST = "$(BUNDLE_LOADER)"; 426 | WRAPPER_EXTENSION = xctest; 427 | }; 428 | name = Debug; 429 | }; 430 | 68D7C20618347A08006B418C /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 434 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WKPagesScrollView.app/WKPagesScrollView"; 435 | FRAMEWORK_SEARCH_PATHS = ( 436 | "$(SDKROOT)/Developer/Library/Frameworks", 437 | "$(inherited)", 438 | "$(DEVELOPER_FRAMEWORKS_DIR)", 439 | ); 440 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 441 | GCC_PREFIX_HEADER = "WKPagesScrollView/WKPagesScrollView-Prefix.pch"; 442 | INFOPLIST_FILE = "WKPagesScrollViewTests/WKPagesScrollViewTests-Info.plist"; 443 | PRODUCT_NAME = WKPagesCollectionViewTests; 444 | TEST_HOST = "$(BUNDLE_LOADER)"; 445 | WRAPPER_EXTENSION = xctest; 446 | }; 447 | name = Release; 448 | }; 449 | /* End XCBuildConfiguration section */ 450 | 451 | /* Begin XCConfigurationList section */ 452 | 68D7C1CA18347A08006B418C /* Build configuration list for PBXProject "WKPagesCollectionView" */ = { 453 | isa = XCConfigurationList; 454 | buildConfigurations = ( 455 | 68D7C1FF18347A08006B418C /* Debug */, 456 | 68D7C20018347A08006B418C /* Release */, 457 | ); 458 | defaultConfigurationIsVisible = 0; 459 | defaultConfigurationName = Release; 460 | }; 461 | 68D7C20118347A08006B418C /* Build configuration list for PBXNativeTarget "WKPagesCollectionView" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | 68D7C20218347A08006B418C /* Debug */, 465 | 68D7C20318347A08006B418C /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | 68D7C20418347A08006B418C /* Build configuration list for PBXNativeTarget "WKPagesCollectionViewTests" */ = { 471 | isa = XCConfigurationList; 472 | buildConfigurations = ( 473 | 68D7C20518347A08006B418C /* Debug */, 474 | 68D7C20618347A08006B418C /* Release */, 475 | ); 476 | defaultConfigurationIsVisible = 0; 477 | defaultConfigurationName = Release; 478 | }; 479 | /* End XCConfigurationList section */ 480 | }; 481 | rootObject = 68D7C1C718347A08006B418C /* Project object */; 482 | } 483 | -------------------------------------------------------------------------------- /WKPagesCollectionView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WKPagesCollectionView.xcodeproj/xcuserdata/reynoldqin.xcuserdatad/xcschemes/WKPagesScrollView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /WKPagesCollectionView.xcodeproj/xcuserdata/reynoldqin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WKPagesScrollView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 68D7C1CE18347A08006B418C 16 | 17 | primary 18 | 19 | 20 | 68D7C1EF18347A08006B418C 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /WKPagesScrollView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /WKPagesScrollView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /WKPagesScrollView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "weather-default-bg.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "weather-default-bg@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-0.imageset/weather-default-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adow/WKPagesCollectionView/e4192bb60f039553190b4d1333a3abe14e1fd224/WKPagesScrollView/Images.xcassets/image-0.imageset/weather-default-bg.png -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-0.imageset/weather-default-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adow/WKPagesCollectionView/e4192bb60f039553190b4d1333a3abe14e1fd224/WKPagesScrollView/Images.xcassets/image-0.imageset/weather-default-bg@2x.png -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "IMG_1107.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-1.imageset/IMG_1107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adow/WKPagesCollectionView/e4192bb60f039553190b4d1333a3abe14e1fd224/WKPagesScrollView/Images.xcassets/image-1.imageset/IMG_1107.png -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "IMG_1364.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /WKPagesScrollView/Images.xcassets/image-2.imageset/IMG_1364.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adow/WKPagesCollectionView/e4192bb60f039553190b4d1333a3abe14e1fd224/WKPagesScrollView/Images.xcassets/image-2.imageset/IMG_1364.png -------------------------------------------------------------------------------- /WKPagesScrollView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WKPagesCollectionView.h" 11 | @interface ViewController : UIViewController{ 12 | 13 | } 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /WKPagesScrollView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController (){ 12 | WKPagesCollectionView* _collectionView; 13 | NSMutableArray* _array; 14 | } 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad 21 | { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | _array=[[NSMutableArray alloc]init]; 25 | for (int a=0; a<=30; a++) { 26 | [_array addObject:[NSString stringWithFormat:@"button %d",a]]; 27 | } 28 | _collectionView=[[[WKPagesCollectionView alloc]initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.view.frame.size.height)] autorelease]; 29 | _collectionView.dataSource=self; 30 | _collectionView.delegate=self; 31 | [_collectionView registerClass:[WKPagesCollectionViewCell class] forCellWithReuseIdentifier:@"cell"]; 32 | [self.view addSubview:_collectionView]; 33 | _collectionView.maskShow=YES; 34 | 35 | 36 | UIToolbar* toolBar=[[[UIToolbar alloc]initWithFrame:CGRectMake(0.0f, self.view.frame.size.height-50.0f, self.view.frame.size.width, 50.0f)] autorelease]; 37 | toolBar.barStyle=UIBarStyleBlackTranslucent; 38 | toolBar.translucent=YES; 39 | toolBar.tintColor=[UIColor whiteColor]; 40 | [self.view addSubview:toolBar]; 41 | 42 | 43 | UIBarButtonItem* addButtonItem=[[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(onButtonAdd:)] autorelease]; 44 | toolBar.items=@[ 45 | [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], 46 | addButtonItem, 47 | [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease],]; 48 | } 49 | 50 | - (void)didReceiveMemoryWarning 51 | { 52 | [super didReceiveMemoryWarning]; 53 | // Dispose of any resources that can be recreated. 54 | } 55 | -(void)dealloc{ 56 | [_array release]; 57 | [_collectionView release]; 58 | [super dealloc]; 59 | } 60 | -(IBAction)onButtonTitle:(id)sender{ 61 | NSLog(@"button"); 62 | [_collectionView dismissFromHightLightWithCompletion:^(BOOL finished) { 63 | NSLog(@"dismiss completed"); 64 | }]; 65 | } 66 | -(IBAction)onButtonAdd:(id)sender{ 67 | [_collectionView appendItem]; 68 | } 69 | #pragma mark - UICollectionViewDataSource and UICollectionViewDelegate 70 | -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 71 | return _array.count; 72 | } 73 | -(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 74 | // NSLog(@"cellForItemAtIndexPath:%d",indexPath.row); 75 | static NSString* identity=@"cell"; 76 | WKPagesCollectionViewCell* cell=(WKPagesCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:identity forIndexPath:indexPath]; 77 | cell.collectionView=collectionView; 78 | UIImageView* imageView=[[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"image-0"]] autorelease]; 79 | imageView.frame=self.view.bounds; 80 | [cell.cellContentView addSubview:imageView]; 81 | UIButton* button=[UIButton buttonWithType:UIButtonTypeCustom]; 82 | button.frame=CGRectMake(0, (indexPath.row+1)*10+100, 320, 50.0f); 83 | button.backgroundColor=[UIColor whiteColor]; 84 | [button setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 85 | [button setTitle:_array[indexPath.row] forState:UIControlStateNormal]; 86 | [button addTarget:self action:@selector(onButtonTitle:) forControlEvents:UIControlEventTouchUpInside]; 87 | [cell.cellContentView addSubview:button]; 88 | return cell; 89 | } 90 | #pragma mark WKPagesCollectionViewDataSource 91 | ///追加数据 92 | -(void)willAppendItemInCollectionView:(WKPagesCollectionView *)collectionView{ 93 | [_array addObject:@"new button"]; 94 | } 95 | ///删除数据 96 | -(void)collectionView:(WKPagesCollectionView *)collectionView willRemoveCellAtIndexPath:(NSIndexPath *)indexPath{ 97 | [_array removeObjectAtIndex:indexPath.row]; 98 | } 99 | @end 100 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WK.h: -------------------------------------------------------------------------------- 1 | // 2 | // WK.h 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-25. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #ifndef WKPagesScrollView_WK_h 10 | #define WKPagesScrollView_WK_h 11 | 12 | #define WKPagesCollectionViewPageSpacing 160.0f 13 | 14 | static inline CATransform3D WKFlipCATransform3DMakePerspective(CGPoint center, float disZ) 15 | { 16 | CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, -300.0f); 17 | CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 300.0f); 18 | CATransform3D scale = CATransform3DIdentity; 19 | scale.m34 = -1.0f/disZ; 20 | return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack); 21 | } 22 | static inline CATransform3D WKFlipCATransform3DPerspect(CATransform3D t, CGPoint center, float disZ) 23 | { 24 | return CATransform3DConcat(t, WKFlipCATransform3DMakePerspective(center, disZ)); 25 | } 26 | static inline CATransform3D WKFlipCATransform3DPerspectSimple(CATransform3D t){ 27 | return WKFlipCATransform3DPerspect(t, CGPointMake(0, 0), 1500.0f); 28 | } 29 | static inline CATransform3D WKFlipCATransform3DPerspectSimpleWithRotate(CGFloat degree){ 30 | return WKFlipCATransform3DPerspectSimple(CATransform3DMakeRotation((M_PI*degree/180.0f), 1.0, 0.0, 0.0)); 31 | } 32 | static inline UIImage* makeImageForView(UIView*view){ 33 | double startTime=CFAbsoluteTimeGetCurrent(); 34 | if(UIGraphicsBeginImageContextWithOptions != NULL){ 35 | UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0); 36 | } else { 37 | UIGraphicsBeginImageContext(view.frame.size); 38 | } 39 | [view.layer renderInContext:UIGraphicsGetCurrentContext()]; 40 | UIImage* image=UIGraphicsGetImageFromCurrentImageContext(); 41 | UIGraphicsEndImageContext(); 42 | NSLog(@"makeImage duration:%f", CFAbsoluteTimeGetCurrent()-startTime); 43 | return image; 44 | } 45 | #endif 46 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WKPagesCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKPagesScrollView.h 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | 10 | 11 | #import 12 | #import "WKPagesCollectionViewFlowLayout.h" 13 | #import "WKPagesCollectionViewCell.h" 14 | @class WKPagesCollectionView; 15 | @protocol WKPagesCollectionViewDataSource 16 | ///When you delete a cell is used to delete data 17 | -(void)collectionView:(WKPagesCollectionView*)collectionView willRemoveCellAtIndexPath:(NSIndexPath*)indexPath; 18 | ///Called when additional data 19 | -(void)willAppendItemInCollectionView:(WKPagesCollectionView*)collectionView; 20 | @end 21 | @protocol WKPagesCollectionViewDelegate 22 | @optional 23 | ///When displaying a callback 24 | -(void)collectionView:(WKPagesCollectionView*)collectionView didShownToHightlightAtIndexPath:(NSIndexPath*)indexPath; 25 | ///Return to the original state when the callback 26 | -(void)didDismissFromHightlightOnCollectionView:(WKPagesCollectionView*)collectionView; 27 | @end 28 | @interface WKPagesCollectionView : UICollectionView{ 29 | UIImageView* _maskImageView; 30 | BOOL _maskShow; 31 | } 32 | ///Can I delete? 33 | @property (nonatomic,assign) BOOL canRemove; 34 | ///show mask 35 | @property (nonatomic,assign) BOOL maskShow; 36 | ///are showing 37 | @property (readonly,nonatomic,assign) BOOL isHighLight; 38 | 39 | ///top offscreen margin 40 | @property (nonatomic,assign) CGFloat topOffScreenMargin; 41 | ///duration from normal to highlight state,default is 0.5 second 42 | @property (nonatomic,assign) CGFloat highLightAnimationDuration; 43 | ///duration from highlight to nomral state, default is 0.5 second 44 | @property (nonatomic,assign) CGFloat dismisalAnimationDuration; 45 | #pragma mark - Action 46 | ///show 47 | -(void)showCellToHighLightAtIndexPath:(NSIndexPath*)indexPath completion:(void(^)(BOOL finished))completion; 48 | ///No animation display state, there will be didShowCellToHightLight callback 49 | -(void)showCellToHighLightAtIndexPath:(NSIndexPath*)indexPath; 50 | ///Scroll back to its original state 51 | -(void)dismissFromHightLightWithCompletion:(void(^)(BOOL finished))completion; 52 | ///Additional content 53 | -(void)appendItem; 54 | @end 55 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WKPagesCollectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKPagesScrollView.m 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import "WKPagesCollectionView.h" 10 | 11 | CGFloat const TOP_OFFSCREEN_MARGIN = 120; 12 | 13 | @implementation WKPagesCollectionView 14 | @dynamic maskShow; 15 | 16 | 17 | - (CGFloat)topOffScreenMargin 18 | { 19 | if (!_topOffScreenMargin) { 20 | _topOffScreenMargin = TOP_OFFSCREEN_MARGIN; 21 | } 22 | return _topOffScreenMargin; 23 | } 24 | -(id)initWithFrame:(CGRect)frame{ 25 | WKPagesCollectionViewFlowLayout* flowLayout=[[[WKPagesCollectionViewFlowLayout alloc ] init] autorelease]; 26 | CGRect realFrame = CGRectMake(frame.origin.x, frame.origin.y-self.topOffScreenMargin, 27 | frame.size.width, frame.size.height + self.topOffScreenMargin); 28 | self = [super initWithFrame:realFrame collectionViewLayout:flowLayout]; 29 | if (self){ 30 | self.contentInset=UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f); 31 | self.highLightAnimationDuration=0.5f; 32 | self.dismisalAnimationDuration=0.5f; 33 | } 34 | return self; 35 | } 36 | -(void)dealloc{ 37 | [_maskImageView release]; 38 | [super dealloc]; 39 | } 40 | -(void)setHidden:(BOOL)hidden{ 41 | [super setHidden:hidden]; 42 | if (hidden){ 43 | if (_maskImageView){ 44 | [_maskImageView removeFromSuperview]; 45 | _maskImageView=nil; 46 | } 47 | } 48 | else{ 49 | [self setMaskShow:_maskShow]; 50 | } 51 | } 52 | #pragma mark - Mask 53 | -(void)setMaskShow:(BOOL)maskShow{ 54 | _maskShow=maskShow; 55 | if (maskShow){ 56 | if (!_maskImageView){ 57 | _maskImageView=[[UIImageView alloc]initWithImage:[self makeGradientImage]]; 58 | [self.superview addSubview:_maskImageView]; 59 | } 60 | _maskImageView.hidden=NO; 61 | } 62 | else{ 63 | _maskImageView.hidden=YES; 64 | } 65 | } 66 | -(BOOL)maskShow{ 67 | return _maskShow; 68 | } 69 | -(UIImage*)makeGradientImage{ 70 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1.0f); 71 | CGContextRef context=UIGraphicsGetCurrentContext(); 72 | CGContextSaveGState(context); 73 | 74 | CGGradientRef myGradient; 75 | CGColorSpaceRef myColorspace; 76 | size_t num_locations = 3; 77 | CGFloat locations[] = { 0.0,0.6f, 1.0}; 78 | CGFloat components[] = { 79 | 0.0,0.0,0.0,0.0, // Start color 80 | 0.0,0.0,0.0,0.2, 81 | 0.0,0.0,0.0,0.6}; // End color 82 | myColorspace = CGColorSpaceCreateDeviceRGB(); 83 | myGradient = CGGradientCreateWithColorComponents (myColorspace, components, 84 | locations, num_locations); 85 | myGradient = CGGradientCreateWithColorComponents (myColorspace, components, 86 | locations, num_locations); 87 | CGPoint myStartPoint={self.bounds.size.width/2,self.bounds.size.height/2}; 88 | CGFloat myStartRadius=0, myEndRadius=self.bounds.size.width; 89 | CGContextDrawRadialGradient (context, myGradient, myStartPoint, 90 | myStartRadius, myStartPoint, myEndRadius, 91 | kCGGradientDrawsAfterEndLocation); 92 | 93 | UIImage* image=UIGraphicsGetImageFromCurrentImageContext(); 94 | CGContextRestoreGState(context); 95 | CGColorSpaceRelease(myColorspace); 96 | CGGradientRelease(myGradient); 97 | UIGraphicsEndImageContext(); 98 | return image; 99 | } 100 | #pragma mark - Actions 101 | ///Display status 102 | -(void)showCellToHighLightAtIndexPath:(NSIndexPath *)indexPath completion:(void (^)(BOOL))completion{ 103 | NSLog(@"row:%d",indexPath.row); 104 | if (_isHighLight){ 105 | return; 106 | } 107 | ///如果在可视范围内就不要滚动了 108 | BOOL no_scroll=NO; 109 | NSArray* visibleIndexPaths=[self indexPathsForVisibleItems]; 110 | for (NSIndexPath* indexPath in visibleIndexPaths) { 111 | if (indexPath.row==indexPath.row){ 112 | no_scroll=YES; 113 | } 114 | } 115 | if (!no_scroll){ 116 | [self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; 117 | } 118 | double delayInSeconds = 0.3f; 119 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 120 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 121 | self.maskShow=NO; 122 | self.scrollEnabled=NO; 123 | [UIView animateWithDuration:self.highLightAnimationDuration delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{ 124 | [self.visibleCells enumerateObjectsUsingBlock:^(WKPagesCollectionViewCell* cell, NSUInteger idx, BOOL *stop) { 125 | NSIndexPath* visibleIndexPath=[self indexPathForCell:cell]; 126 | if (visibleIndexPath.row==indexPath.row){ 127 | cell.showingState=WKPagesCollectionViewCellShowingStateHightlight; 128 | } 129 | else if (visibleIndexPath.rowindexPath.row){ 133 | NSLog(@"indexPath:%d,visibleIndexPath:%d",indexPath.row,visibleIndexPath.row); 134 | cell.showingState=WKPagesCollectionViewCellShowingStateBackToBottom; 135 | } 136 | else{ 137 | cell.showingState=WKPagesCollectionViewCellShowingStateNormal; 138 | } 139 | }]; 140 | } completion:^(BOOL finished) { 141 | _isHighLight=YES; 142 | completion(finished); 143 | if ([self.delegate respondsToSelector:@selector(collectionView:didShownToHightlightAtIndexPath:)]){ 144 | [(id)self.delegate collectionView:self didShownToHightlightAtIndexPath:indexPath]; 145 | } 146 | }]; 147 | }); 148 | 149 | } 150 | -(void)showCellToHighLightAtIndexPath:(NSIndexPath *)indexPath{ 151 | if (_isHighLight){ 152 | return; 153 | } 154 | [self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO]; 155 | double delayInSeconds = 0.01f; 156 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 157 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 158 | self.maskShow=NO; 159 | self.scrollEnabled=NO; 160 | [self.visibleCells enumerateObjectsUsingBlock:^(WKPagesCollectionViewCell* cell, NSUInteger idx, BOOL *stop) { 161 | NSIndexPath* visibleIndexPath=[self indexPathForCell:cell]; 162 | if (visibleIndexPath.row==indexPath.row){ 163 | cell.showingState=WKPagesCollectionViewCellShowingStateHightlight; 164 | } 165 | else if (visibleIndexPath.rowindexPath.row){ 169 | cell.showingState=WKPagesCollectionViewCellShowingStateBackToBottom; 170 | } 171 | else{ 172 | cell.showingState=WKPagesCollectionViewCellShowingStateNormal; 173 | } 174 | }]; 175 | _isHighLight=YES; 176 | }); 177 | 178 | } 179 | ///Back to the original state 180 | -(void)dismissFromHightLightWithCompletion:(void (^)(BOOL))completion{ 181 | self.maskShow=YES; 182 | if (!_isHighLight) 183 | return; 184 | [UIView animateWithDuration:self.dismisalAnimationDuration delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{ 185 | [self.visibleCells enumerateObjectsUsingBlock:^(WKPagesCollectionViewCell* cell, NSUInteger idx, BOOL *stop) { 186 | cell.showingState=WKPagesCollectionViewCellShowingStateNormal; 187 | }]; 188 | } completion:^(BOOL finished) { 189 | self.scrollEnabled=YES; 190 | _isHighLight=NO; 191 | completion(finished); 192 | if ([self.delegate respondsToSelector:@selector(didDismissFromHightlightOnCollectionView:)]){ 193 | [(id)self.delegate didDismissFromHightlightOnCollectionView:self]; 194 | } 195 | }]; 196 | } 197 | ///Append a page 198 | -(void)appendItem{ 199 | if (self.isHighLight){ 200 | [self dismissFromHightLightWithCompletion:^(BOOL finished) { 201 | [self _addNewPage]; 202 | }]; 203 | } 204 | else{ 205 | [self _addNewPage]; 206 | } 207 | } 208 | ///Adding a 209 | -(void)_addNewPage{ 210 | int total=[self numberOfItemsInSection:0]; 211 | if (total > 0) { 212 | [self scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:total-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:YES]; 213 | } 214 | 215 | double delayInSeconds = 0.3f; 216 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 217 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 218 | ///Add Data 219 | [(id)self.dataSource willAppendItemInCollectionView:self]; 220 | int lastRow=total; 221 | NSIndexPath* insertIndexPath=[NSIndexPath indexPathForItem:lastRow inSection:0]; 222 | [self performBatchUpdates:^{ 223 | [self insertItemsAtIndexPaths:@[insertIndexPath]]; 224 | } completion:^(BOOL finished) { 225 | double delayInSeconds = 0.3f; 226 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 227 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 228 | [self showCellToHighLightAtIndexPath:insertIndexPath completion:^(BOOL finished) { 229 | 230 | }]; 231 | }); 232 | 233 | }]; 234 | }); 235 | } 236 | #pragma mark - UIView and UICollectionView 237 | -(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ 238 | UIView* view=[super hitTest:point withEvent:event]; 239 | if (!view){ 240 | return nil; 241 | } 242 | if (view==self){ 243 | for (WKPagesCollectionViewCell* cell in self.visibleCells) { 244 | if (cell.showingState==WKPagesCollectionViewCellShowingStateHightlight){ 245 | return cell.cellContentView;///Events should only be passed to this layer 246 | } 247 | } 248 | } 249 | return view; 250 | // UIView* view=[super hitTest:point withEvent:event]; 251 | // NSLog(@"%@,%d",NSStringFromClass([view class]),view.tag); 252 | // return view; 253 | } 254 | @end 255 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WKPagesCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKPagesCollectionViewCell.h 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-15. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import 10 | typedef enum WKPagesCollectionViewCellShowingState:NSUInteger{ 11 | WKPagesCollectionViewCellShowingStateNormal=0, 12 | WKPagesCollectionViewCellShowingStateHightlight=1, 13 | WKPagesCollectionViewCellShowingStateBackToTop=2, 14 | WKPagesCollectionViewCellShowingStateBackToBottom=3, 15 | } WKPagesCollectionViewCellShowingState; 16 | @interface WKPagesCollectionViewCell : UICollectionViewCell{ 17 | WKPagesCollectionViewCellShowingState _showingState; 18 | UITapGestureRecognizer* _tapGesture; 19 | UIScrollView* _scrollView; 20 | // UIImageView* _maskImageView; 21 | } 22 | ///Position the normal state 23 | @property (nonatomic,assign) CATransform3D normalTransform; 24 | ///Position the normal state 25 | @property (nonatomic,assign) CGRect normalFrame; 26 | ///Display status 27 | @property (nonatomic,assign) WKPagesCollectionViewCellShowingState showingState; 28 | ///Quote collectionView 29 | @property (nonatomic,assign) UICollectionView* collectionView; 30 | @property (nonatomic,retain) UIView* cellContentView; 31 | -(UIImage*)makeGradientImage; 32 | @end 33 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WKPagesCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKPagesCollectionViewCell.m 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-15. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import "WKPagesCollectionViewCell.h" 10 | #import "WKPagesCollectionView.h" 11 | @implementation WKPagesCollectionViewCell 12 | @dynamic showingState; 13 | - (id)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | // Initialization code 18 | self.clipsToBounds=NO; 19 | self.backgroundColor=[UIColor clearColor]; 20 | self.contentView.tag=100; 21 | CGRect rect=CGRectMake(0.0f, 0.0f, 22 | [UIScreen mainScreen].bounds.size.width, 23 | [UIScreen mainScreen].bounds.size.height); 24 | if (!_scrollView){ 25 | _scrollView=[[UIScrollView alloc]initWithFrame:rect]; 26 | _scrollView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 27 | _scrollView.clipsToBounds=NO; 28 | _scrollView.backgroundColor=[UIColor clearColor]; 29 | _scrollView.showsVerticalScrollIndicator=NO; 30 | _scrollView.showsHorizontalScrollIndicator=YES; 31 | _scrollView.contentSize=CGSizeMake(_scrollView.frame.size.width+1, _scrollView.frame.size.height); 32 | _scrollView.delegate=self; 33 | [self.contentView addSubview:_scrollView]; 34 | _scrollView.tag=101; 35 | } 36 | if (!_cellContentView){ 37 | _cellContentView=[[UIView alloc]initWithFrame:rect]; 38 | _cellContentView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 39 | [_scrollView addSubview:_cellContentView]; 40 | _cellContentView.tag=102; 41 | } 42 | 43 | if (!_tapGesture){ 44 | _tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(onTapGesture:)]; 45 | [_scrollView addGestureRecognizer:_tapGesture]; 46 | } 47 | } 48 | return self; 49 | } 50 | 51 | /* 52 | // Only override drawRect: if you perform custom drawing. 53 | // An empty implementation adversely affects performance during animation. 54 | - (void)drawRect:(CGRect)rect 55 | { 56 | // Drawing code 57 | } 58 | */ 59 | -(void)dealloc{ 60 | [_tapGesture release]; 61 | [_cellContentView release]; 62 | [_scrollView release]; 63 | [super dealloc]; 64 | } 65 | -(void)prepareForReuse{ 66 | [super prepareForReuse]; 67 | for (UIView* view in _cellContentView.subviews) { 68 | [view removeFromSuperview]; 69 | } 70 | 71 | } 72 | -(IBAction)onTapGesture:(UITapGestureRecognizer*)tapGesture{ 73 | NSIndexPath* indexPath=[self.collectionView indexPathForCell:self]; 74 | // NSLog(@"row:%d",indexPath.row); 75 | // [self.collectionView.delegate collectionView:self.collectionView didSelectItemAtIndexPath:indexPath]; 76 | [(WKPagesCollectionView*)self.collectionView showCellToHighLightAtIndexPath:indexPath completion:^(BOOL finished) { 77 | NSLog(@"highlight completed"); 78 | }]; 79 | } 80 | #pragma mark - Properties 81 | -(void)setShowingState:(WKPagesCollectionViewCellShowingState)showingState{ 82 | if (_showingState==showingState) 83 | return; 84 | _showingState=showingState; 85 | WKPagesCollectionViewFlowLayout* collectionLayout=(WKPagesCollectionViewFlowLayout*)self.collectionView.collectionViewLayout; 86 | CGFloat pageHeight=collectionLayout.pageHeight; 87 | CGFloat topMargin=[(WKPagesCollectionView*)self.collectionView topOffScreenMargin]; 88 | switch (showingState) { 89 | case WKPagesCollectionViewCellShowingStateHightlight:{ 90 | self.normalTransform=self.layer.transform;///The original location of the first record 91 | _scrollView.scrollEnabled=NO; 92 | NSIndexPath* indexPath=[self.collectionView indexPathForCell:self]; 93 | CGFloat moveY=self.collectionView.contentOffset.y-(WKPagesCollectionViewPageSpacing)*indexPath.row +topMargin; 94 | CATransform3D moveTransform=CATransform3DMakeTranslation(0.0f, moveY, 0.0f); 95 | self.layer.transform=moveTransform; 96 | } 97 | break; 98 | case WKPagesCollectionViewCellShowingStateBackToTop:{ 99 | self.normalTransform=self.layer.transform;///The original location of the first record 100 | _scrollView.scrollEnabled=NO; 101 | CATransform3D moveTransform=CATransform3DMakeTranslation(0, -1*pageHeight-topMargin, 0); 102 | self.layer.transform=CATransform3DConcat(CATransform3DIdentity, moveTransform); 103 | } 104 | break; 105 | case WKPagesCollectionViewCellShowingStateBackToBottom:{ 106 | self.normalTransform=self.layer.transform;///The original location of the first record 107 | _scrollView.scrollEnabled=NO; 108 | CATransform3D moveTransform=CATransform3DMakeTranslation(0, pageHeight+topMargin, 0); 109 | self.layer.transform=CATransform3DConcat(CATransform3DIdentity, moveTransform); 110 | } 111 | break; 112 | case WKPagesCollectionViewCellShowingStateNormal:{ 113 | self.layer.transform=self.normalTransform; 114 | _scrollView.scrollEnabled=YES; 115 | } 116 | break; 117 | default: 118 | break; 119 | } 120 | 121 | 122 | 123 | } 124 | -(WKPagesCollectionViewCellShowingState)showingState{ 125 | return _showingState; 126 | } 127 | #pragma mark - UIScrollViewDelegate 128 | -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ 129 | 130 | } 131 | -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ 132 | 133 | } 134 | -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ 135 | if (self.showingState==WKPagesCollectionViewCellShowingStateNormal){ 136 | if (scrollView.contentOffset.x>=90.0f){ 137 | NSIndexPath* indexPath=[self.collectionView indexPathForCell:self]; 138 | NSLog(@"delete cell at %d",indexPath.row); 139 | //self.alpha=0.0f; 140 | ///Delete data 141 | id pagesDataSource=(id)self.collectionView.dataSource; 142 | [pagesDataSource collectionView:(WKPagesCollectionView*)self.collectionView willRemoveCellAtIndexPath:indexPath]; 143 | ///Animation 144 | [self.collectionView performBatchUpdates:^{ 145 | [self.collectionView deleteItemsAtIndexPaths:@[indexPath,]]; 146 | } completion:^(BOOL finished) { 147 | 148 | }]; 149 | } 150 | } 151 | } 152 | #pragma mark - Image 153 | -(UIImage*)makeGradientImage{ 154 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1.0f); 155 | CGContextRef context=UIGraphicsGetCurrentContext(); 156 | CGContextSaveGState(context); 157 | 158 | CGGradientRef myGradient; 159 | CGColorSpaceRef myColorspace; 160 | size_t num_locations = 2; 161 | CGFloat locations[2] = { 0.0, 1.0 }; 162 | CGFloat components[8] = { 0.0,0.0,0.0, 0.0, // Start color 163 | 0.0,0.0,0.0,1.0}; // End color 164 | myColorspace = CGColorSpaceCreateDeviceRGB(); 165 | myGradient = CGGradientCreateWithColorComponents (myColorspace, components, 166 | locations, num_locations); 167 | CGContextDrawLinearGradient(context, myGradient, CGPointMake(self.bounds.size.width/2, 100.0f), CGPointMake(self.bounds.size.width/2, self.bounds.size.height-100.0f), kCGGradientDrawsAfterEndLocation); 168 | 169 | UIImage* image=UIGraphicsGetImageFromCurrentImageContext(); 170 | CGContextRestoreGState(context); 171 | CGColorSpaceRelease(myColorspace); 172 | CGGradientRelease(myGradient); 173 | UIGraphicsEndImageContext(); 174 | return image; 175 | } 176 | @end 177 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WKPagesCollectionViewFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKPagesCollectionViewFlowLayout.h 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-15. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WK.h" 11 | 12 | @interface WKPagesCollectionViewFlowLayout : UICollectionViewFlowLayout{ 13 | 14 | } 15 | @property (nonatomic,readonly) CGFloat pageHeight; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /WKPagesScrollView/WKPagesCollectionView/WKPagesCollectionViewFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKPagesCollectionViewFlowLayout.m 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-15. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | 10 | #import "WKPagesCollectionViewFlowLayout.h" 11 | #import "WKPagesCollectionView.h" 12 | #define RotateDegree -60.0f 13 | @interface WKPagesCollectionViewFlowLayout() 14 | @property (nonatomic,retain) NSMutableArray* deleteIndexPaths; 15 | @property (nonatomic,retain) NSMutableArray* insertIndexPaths; 16 | @end 17 | @implementation WKPagesCollectionViewFlowLayout{ 18 | 19 | } 20 | -(id)init{ 21 | self=[super init]; 22 | if (self){ 23 | 24 | } 25 | return self; 26 | } 27 | -(void)prepareLayout 28 | { 29 | [super prepareLayout]; 30 | self.itemSize=[UIScreen mainScreen].bounds.size; 31 | self.minimumLineSpacing=-1*(self.itemSize.height-WKPagesCollectionViewPageSpacing); 32 | self.scrollDirection=UICollectionViewScrollDirectionVertical; 33 | } 34 | -(CGFloat)pageHeight{ 35 | return self.itemSize.height; 36 | } 37 | -(CGSize)collectionViewContentSize{ 38 | 39 | int numberOfItems=[self.collectionView numberOfItemsInSection:0]; 40 | // CGFloat topMargin=[(WKPagesCollectionView*)self.collectionView topOffScreenMargin]; 41 | CGFloat contentHeight=numberOfItems*self.pageHeight+self.self.minimumLineSpacing*(numberOfItems-1); 42 | contentHeight=fmaxf(contentHeight, self.collectionView.frame.size.height); 43 | return CGSizeMake(self.collectionView.frame.size.width,contentHeight); 44 | } 45 | 46 | - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)oldBounds 47 | { 48 | return YES; 49 | } 50 | -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path 51 | { 52 | // NSLog(@"layoutAttributesForItemAtIndexPath:%d",path.row); 53 | UICollectionViewLayoutAttributes* attributes=[super layoutAttributesForItemAtIndexPath:path]; 54 | [self makeRotateTransformForAttributes:attributes]; 55 | return attributes; 56 | } 57 | -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect 58 | { 59 | // NSLog(@"layoutAttributesForElementsInRect:%@",NSStringFromCGRect(rect)); 60 | NSArray* array = [super layoutAttributesForElementsInRect:rect]; 61 | for (UICollectionViewLayoutAttributes* attributes in array) { 62 | [self makeRotateTransformForAttributes:attributes]; 63 | } 64 | return array; 65 | } 66 | #pragma mark Collection Update 67 | -(void)prepareForCollectionViewUpdates:(NSArray *)updateItems{ 68 | // NSLog(@"prepareForCollectionViewUpdates"); 69 | [super prepareForCollectionViewUpdates:updateItems]; 70 | self.deleteIndexPaths=[NSMutableArray array]; 71 | self.insertIndexPaths=[NSMutableArray array]; 72 | for (UICollectionViewUpdateItem* updateItem in updateItems) { 73 | if (updateItem.updateAction==UICollectionUpdateActionDelete){ 74 | [self.deleteIndexPaths addObject:updateItem.indexPathBeforeUpdate]; 75 | } 76 | if (updateItem.updateAction==UICollectionUpdateActionInsert){ 77 | [self.insertIndexPaths addObject:updateItem.indexPathAfterUpdate]; 78 | } 79 | } 80 | } 81 | -(void)finalizeCollectionViewUpdates{ 82 | // NSLog(@"finalizeCollectionViewUpdates"); 83 | [super finalizeCollectionViewUpdates]; 84 | self.deleteIndexPaths=nil; 85 | self.insertIndexPaths=nil; 86 | } 87 | -(UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ 88 | UICollectionViewLayoutAttributes* attributes=[super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; 89 | // NSLog(@"initialLayoutAttributesForAppearingItemAtIndexPath:%d",itemIndexPath.row); 90 | if ([self.insertIndexPaths containsObject:itemIndexPath]){ 91 | if (!attributes) 92 | attributes=[self layoutAttributesForItemAtIndexPath:itemIndexPath]; 93 | CATransform3D rotateTransform=WKFlipCATransform3DPerspectSimpleWithRotate(-90.0f); 94 | attributes.transform3D=rotateTransform; 95 | } 96 | return attributes; 97 | } 98 | -(UICollectionViewLayoutAttributes*)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ 99 | NSLog(@"finalLayoutAttributesForDisappearingItemAtIndexPath:%d",itemIndexPath.row); 100 | UICollectionViewLayoutAttributes* attributes=[super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath]; 101 | if ([self.deleteIndexPaths containsObject:itemIndexPath]){ 102 | if (!attributes){ 103 | attributes=[self layoutAttributesForItemAtIndexPath:itemIndexPath]; 104 | } 105 | CATransform3D moveTransform=CATransform3DMakeTranslation(-320.0f, 0.0f, 0.0f); 106 | attributes.transform3D=CATransform3DConcat(attributes.transform3D, moveTransform); 107 | } 108 | return attributes; 109 | 110 | } 111 | ///As attribute set up a new perspective 112 | -(void)makeRotateTransformForAttributes:(UICollectionViewLayoutAttributes*)attributes{ 113 | attributes.zIndex=attributes.indexPath.row;///To set the zIndex,Otherwise, there will be NO blocking order 114 | CGFloat distance=attributes.frame.origin.y-self.collectionView.contentOffset.y; 115 | CGFloat normalizedDistance = distance / self.collectionView.frame.size.height; 116 | normalizedDistance=fmaxf(normalizedDistance, 0.0f); 117 | CGFloat rotate=RotateDegree+20.0f*normalizedDistance; 118 | //CGFloat rotate=RotateDegree; 119 | // NSLog(@"makeRotateTransformForAttributes:row:%d,normalizedDistance:%f,rotate:%f", 120 | // attributes.indexPath.row,normalizedDistance,rotate); 121 | ///Angle and angle will cross a small cell, even if you set zIndex is useless here to set the angle of the bottom of the cell is growing 122 | CATransform3D rotateTransform=WKFlipCATransform3DPerspectSimpleWithRotate(rotate); 123 | attributes.transform3D=rotateTransform; 124 | 125 | } 126 | @end -------------------------------------------------------------------------------- /WKPagesScrollView/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /WKPagesScrollView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WKPagesScrollView 4 | // 5 | // Created by 秦 道平 on 13-11-14. 6 | // Copyright (c) 2013年 秦 道平. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | --------------------------------------------------------------------------------