├── LICENSE
├── README.md
├── Utilities
├── Color.h
└── Color.m
├── iCards.podspec
├── iCards.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── admin.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── admin.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── iCards.xcscheme
│ └── xcschememanagement.plist
├── iCards
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── ViewController.h
├── ViewController.m
└── main.m
└── src
├── iCards.h
└── iCards.m
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Rangding Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iCards
2 | A container of views (like cards) can be dragged!
3 | ---
4 | 视图容器,视图以卡片形式层叠放置,可滑动。
5 | ---
6 | There are only visible cards in memory, after you drag and removed the top one, it will be reused as the last one.
7 | 内存中只会生成可见的卡片,顶部的卡片被划走之后,会作为最后一张卡片循环利用。
8 |
9 | You can find a Swift version here:
10 | 你可以在这里找到Swift版:[SwipeableCards](https://github.com/DingHub/SwipeableCards)
11 |
12 | 这里有一篇:[《探索之旅:代理原理》](http://www.swifthumb.com/thread-14968-1-1.html),可作为iCards的详细说明文档。
13 |
14 | pod surpported:
15 | 支持pod :
16 | ```
17 | target ‘xxx’ do
18 | pod ‘iCards’
19 | end
20 | ```
21 |
22 | 
23 | 
24 | 
25 |
26 | Usage:
27 | ---
28 | Here is an example:
29 | 用法示例:
30 |
31 | ```
32 | #import "iCards.h"
33 | ```
34 | ```
35 | @property (weak, nonatomic) IBOutlet iCards *cards;
36 | @property (nonatomic, strong) NSMutableArray *cardsData;
37 | ```
38 | ```objective-c
39 | - (void)viewDidLoad {
40 | [super viewDidLoad];
41 | [self makeCardsData];
42 | self.cards.dataSource = self;
43 | self.cards.delegate = self;
44 | }
45 | - (void)makeCardsData {
46 | for (int i=0; i<100; i++) {
47 | [self.cardsData addObject:@(i)];
48 | }
49 | }
50 | - (NSMutableArray *)cardsData {
51 | if (_cardsData == nil) {
52 | _cardsData = [NSMutableArray array];
53 | }
54 | return _cardsData;
55 | }
56 |
57 | #pragma mark - iCardsDataSource methods
58 |
59 | - (NSInteger)numberOfItemsInCards:(iCards *)cards {
60 | return self.cardsData.count;
61 | }
62 |
63 | - (UIView *)cards:(iCards *)cards viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view {
64 | UILabel *label = (UILabel *)view;
65 | if (label == nil) {
66 | CGSize size = cards.frame.size;
67 | CGRect labelFrame = CGRectMake(0, 0, size.width - 30, size.height - 20);
68 | label = [[UILabel alloc] initWithFrame:labelFrame];
69 | label.textAlignment = NSTextAlignmentCenter;
70 | label.layer.cornerRadius = 5;
71 | }
72 | label.text = [self.cardsData[index] stringValue];
73 | label.layer.backgroundColor = [Color randomColor].CGColor;
74 | return label;
75 | }
76 |
77 | #pragma mark - iCardsDelegate methods
78 |
79 | - (void)cards:(iCards *)cards beforeSwipingItemAtIndex:(NSInteger)index {
80 | NSLog(@"Begin swiping card %ld!", index);
81 | }
82 |
83 | - (void)cards:(iCards *)cards didLeftRemovedItemAtIndex:(NSInteger)index {
84 | NSLog(@"<--%ld", index);
85 | }
86 |
87 | - (void)cards:(iCards *)cards didRightRemovedItemAtIndex:(NSInteger)index {
88 | NSLog(@"%ld-->", index);
89 | }
90 |
91 | - (void)cards:(iCards *)cards didRemovedItemAtIndex:(NSInteger)index {
92 | NSLog(@"index of removed card: %ld", index);
93 | }
94 |
95 |
96 | ```
97 |
--------------------------------------------------------------------------------
/Utilities/Color.h:
--------------------------------------------------------------------------------
1 | //
2 | // Color.h
3 | // iOSProjectFrame
4 | //
5 | // Created by admin on 16/5/21.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #define Color UIColor
10 |
11 | @interface Color (HexColorAddition)
12 |
13 | + (Color *)randomColor;
14 |
15 | + (Color *)colorWithHexString:(NSString *)hexString;
16 | + (Color *)colorWithHexString:(NSString *)hexString alpha:(CGFloat)alpha;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Utilities/Color.m:
--------------------------------------------------------------------------------
1 | //
2 | // Color.m
3 | // iOSProjectFrame
4 | //
5 | // Created by admin on 16/5/21.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 | #import
9 | #import "Color.h"
10 |
11 | @implementation Color (HexColorAddition)
12 |
13 | + (Color *)randomColor {
14 | CGFloat red = arc4random_uniform(256)/255.0;
15 | CGFloat green = arc4random_uniform(256)/255.0;
16 | CGFloat blue = arc4random_uniform(256)/255.0;
17 | return [Color colorWithRed:red green:green blue:blue alpha:1];
18 | }
19 |
20 | + (Color *)colorWithHexString:(NSString *)hexString {
21 | return [[self class] colorWithHexString:hexString alpha:1.0];
22 | }
23 |
24 | + (Color *)colorWithHexString:(NSString *)hexString alpha:(CGFloat)alpha {
25 | if (hexString.length == 0) {
26 | return nil;
27 | }
28 |
29 | // Check for hash and add the missing hash
30 | if('#' != [hexString characterAtIndex:0]) {
31 | hexString = [NSString stringWithFormat:@"#%@", hexString];
32 | }
33 |
34 | // check for string length
35 | assert(7 == hexString.length || 4 == hexString.length);
36 |
37 | // check for 3 character HexStrings
38 | hexString = [[self class] hexStringTransformFromThreeCharacters:hexString];
39 |
40 | NSString *redHex = [NSString stringWithFormat:@"0x%@", [hexString substringWithRange:NSMakeRange(1, 2)]];
41 | unsigned redInt = [[self class] hexValueToUnsigned:redHex];
42 |
43 | NSString *greenHex = [NSString stringWithFormat:@"0x%@", [hexString substringWithRange:NSMakeRange(3, 2)]];
44 | unsigned greenInt = [[self class] hexValueToUnsigned:greenHex];
45 |
46 | NSString *blueHex = [NSString stringWithFormat:@"0x%@", [hexString substringWithRange:NSMakeRange(5, 2)]];
47 | unsigned blueInt = [[self class] hexValueToUnsigned:blueHex];
48 |
49 | Color *color = [Color colorWith8BitRed:redInt green:greenInt blue:blueInt alpha:alpha];
50 |
51 | return color;
52 | }
53 |
54 | + (Color *)colorWith8BitRed:(NSInteger)red green:(NSInteger)green blue:(NSInteger)blue alpha:(CGFloat)alpha {
55 | Color *color = nil;
56 | color = [Color colorWithRed:(float)red/255 green:(float)green/255 blue:(float)blue/255 alpha:alpha];
57 | return color;
58 | }
59 |
60 | + (NSString *)hexStringTransformFromThreeCharacters:(NSString *)hexString {
61 | if(hexString.length == 4) {
62 | hexString = [NSString stringWithFormat:@"#%@%@%@%@%@%@",
63 | [hexString substringWithRange:NSMakeRange(1, 1)],[hexString substringWithRange:NSMakeRange(1, 1)],
64 | [hexString substringWithRange:NSMakeRange(2, 1)],[hexString substringWithRange:NSMakeRange(2, 1)],
65 | [hexString substringWithRange:NSMakeRange(3, 1)],[hexString substringWithRange:NSMakeRange(3, 1)]];
66 | }
67 | return hexString;
68 | }
69 |
70 | + (unsigned)hexValueToUnsigned:(NSString *)hexValue {
71 | unsigned value = 0;
72 | NSScanner *hexValueScanner = [NSScanner scannerWithString:hexValue];
73 | [hexValueScanner scanHexInt:&value];
74 | return value;
75 | }
76 |
77 |
78 | @end
79 |
--------------------------------------------------------------------------------
/iCards.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "iCards"
4 | s.version = "1.1.0"
5 | s.license = "Copyright (c) 2016 Ding"
6 | s.summary = "A containner of views (like cards) can be dragged!"
7 | s.homepage = "https://github.com/DingHub/iCards"
8 | s.license = "MIT"
9 | s.author = { "DingHub" => "love-nankai@163.com" }
10 | s.source = { :git => "https://github.com/DingHub/iCards.git", :tag => "1.1.0" }
11 | s.source_files = "src/iCards.{h,m}"
12 | s.platform = :ios
13 | s.platform = :ios, "7.0"
14 | s.requires_arc = true
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/iCards.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 843746DB1D45B12B0014D5C6 /* Color.m in Sources */ = {isa = PBXBuildFile; fileRef = 843746DA1D45B12B0014D5C6 /* Color.m */; };
11 | 848884531CC099CF0092486A /* iCards.m in Sources */ = {isa = PBXBuildFile; fileRef = 848884521CC099CF0092486A /* iCards.m */; };
12 | 84C500D11CB4D24E00A2156C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C500D01CB4D24E00A2156C /* main.m */; };
13 | 84C500D41CB4D24E00A2156C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C500D31CB4D24E00A2156C /* AppDelegate.m */; };
14 | 84C500D71CB4D24E00A2156C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C500D61CB4D24E00A2156C /* ViewController.m */; };
15 | 84C500DA1CB4D24E00A2156C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84C500D81CB4D24E00A2156C /* Main.storyboard */; };
16 | 84C500DC1CB4D24E00A2156C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84C500DB1CB4D24E00A2156C /* Assets.xcassets */; };
17 | 84C500DF1CB4D24E00A2156C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84C500DD1CB4D24E00A2156C /* LaunchScreen.storyboard */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 843746D91D45B12B0014D5C6 /* Color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = ""; };
22 | 843746DA1D45B12B0014D5C6 /* Color.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Color.m; sourceTree = ""; };
23 | 848884511CC099CF0092486A /* iCards.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iCards.h; sourceTree = ""; };
24 | 848884521CC099CF0092486A /* iCards.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iCards.m; sourceTree = ""; };
25 | 84C500CC1CB4D24E00A2156C /* iCards.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iCards.app; sourceTree = BUILT_PRODUCTS_DIR; };
26 | 84C500D01CB4D24E00A2156C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
27 | 84C500D21CB4D24E00A2156C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
28 | 84C500D31CB4D24E00A2156C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
29 | 84C500D51CB4D24E00A2156C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
30 | 84C500D61CB4D24E00A2156C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
31 | 84C500D91CB4D24E00A2156C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
32 | 84C500DB1CB4D24E00A2156C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
33 | 84C500DE1CB4D24E00A2156C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
34 | 84C500E01CB4D24E00A2156C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | 84C500C91CB4D24E00A2156C /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | );
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXFrameworksBuildPhase section */
46 |
47 | /* Begin PBXGroup section */
48 | 843746D81D45B1200014D5C6 /* Utilities */ = {
49 | isa = PBXGroup;
50 | children = (
51 | 843746D91D45B12B0014D5C6 /* Color.h */,
52 | 843746DA1D45B12B0014D5C6 /* Color.m */,
53 | );
54 | path = Utilities;
55 | sourceTree = "";
56 | };
57 | 848884501CC099CF0092486A /* src */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 848884511CC099CF0092486A /* iCards.h */,
61 | 848884521CC099CF0092486A /* iCards.m */,
62 | );
63 | path = src;
64 | sourceTree = "";
65 | };
66 | 84C500C31CB4D24E00A2156C = {
67 | isa = PBXGroup;
68 | children = (
69 | 843746D81D45B1200014D5C6 /* Utilities */,
70 | 848884501CC099CF0092486A /* src */,
71 | 84C500CE1CB4D24E00A2156C /* iCardsExample */,
72 | 84C500CD1CB4D24E00A2156C /* Products */,
73 | );
74 | sourceTree = "";
75 | };
76 | 84C500CD1CB4D24E00A2156C /* Products */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 84C500CC1CB4D24E00A2156C /* iCards.app */,
80 | );
81 | name = Products;
82 | sourceTree = "";
83 | };
84 | 84C500CE1CB4D24E00A2156C /* iCardsExample */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 84C500D21CB4D24E00A2156C /* AppDelegate.h */,
88 | 84C500D31CB4D24E00A2156C /* AppDelegate.m */,
89 | 84C500D51CB4D24E00A2156C /* ViewController.h */,
90 | 84C500D61CB4D24E00A2156C /* ViewController.m */,
91 | 84C500D81CB4D24E00A2156C /* Main.storyboard */,
92 | 84C500DB1CB4D24E00A2156C /* Assets.xcassets */,
93 | 84C500DD1CB4D24E00A2156C /* LaunchScreen.storyboard */,
94 | 84C500E01CB4D24E00A2156C /* Info.plist */,
95 | 84C500CF1CB4D24E00A2156C /* Supporting Files */,
96 | );
97 | name = iCardsExample;
98 | path = iCards;
99 | sourceTree = "";
100 | };
101 | 84C500CF1CB4D24E00A2156C /* Supporting Files */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 84C500D01CB4D24E00A2156C /* main.m */,
105 | );
106 | name = "Supporting Files";
107 | sourceTree = "";
108 | };
109 | /* End PBXGroup section */
110 |
111 | /* Begin PBXNativeTarget section */
112 | 84C500CB1CB4D24E00A2156C /* iCards */ = {
113 | isa = PBXNativeTarget;
114 | buildConfigurationList = 84C500E31CB4D24E00A2156C /* Build configuration list for PBXNativeTarget "iCards" */;
115 | buildPhases = (
116 | 84C500C81CB4D24E00A2156C /* Sources */,
117 | 84C500C91CB4D24E00A2156C /* Frameworks */,
118 | 84C500CA1CB4D24E00A2156C /* Resources */,
119 | );
120 | buildRules = (
121 | );
122 | dependencies = (
123 | );
124 | name = iCards;
125 | productName = iCards;
126 | productReference = 84C500CC1CB4D24E00A2156C /* iCards.app */;
127 | productType = "com.apple.product-type.application";
128 | };
129 | /* End PBXNativeTarget section */
130 |
131 | /* Begin PBXProject section */
132 | 84C500C41CB4D24E00A2156C /* Project object */ = {
133 | isa = PBXProject;
134 | attributes = {
135 | LastUpgradeCheck = 0810;
136 | ORGANIZATIONNAME = Ding;
137 | TargetAttributes = {
138 | 84C500CB1CB4D24E00A2156C = {
139 | CreatedOnToolsVersion = 7.2;
140 | };
141 | };
142 | };
143 | buildConfigurationList = 84C500C71CB4D24E00A2156C /* Build configuration list for PBXProject "iCards" */;
144 | compatibilityVersion = "Xcode 3.2";
145 | developmentRegion = English;
146 | hasScannedForEncodings = 0;
147 | knownRegions = (
148 | en,
149 | Base,
150 | );
151 | mainGroup = 84C500C31CB4D24E00A2156C;
152 | productRefGroup = 84C500CD1CB4D24E00A2156C /* Products */;
153 | projectDirPath = "";
154 | projectRoot = "";
155 | targets = (
156 | 84C500CB1CB4D24E00A2156C /* iCards */,
157 | );
158 | };
159 | /* End PBXProject section */
160 |
161 | /* Begin PBXResourcesBuildPhase section */
162 | 84C500CA1CB4D24E00A2156C /* Resources */ = {
163 | isa = PBXResourcesBuildPhase;
164 | buildActionMask = 2147483647;
165 | files = (
166 | 84C500DF1CB4D24E00A2156C /* LaunchScreen.storyboard in Resources */,
167 | 84C500DC1CB4D24E00A2156C /* Assets.xcassets in Resources */,
168 | 84C500DA1CB4D24E00A2156C /* Main.storyboard in Resources */,
169 | );
170 | runOnlyForDeploymentPostprocessing = 0;
171 | };
172 | /* End PBXResourcesBuildPhase section */
173 |
174 | /* Begin PBXSourcesBuildPhase section */
175 | 84C500C81CB4D24E00A2156C /* Sources */ = {
176 | isa = PBXSourcesBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | 848884531CC099CF0092486A /* iCards.m in Sources */,
180 | 84C500D71CB4D24E00A2156C /* ViewController.m in Sources */,
181 | 843746DB1D45B12B0014D5C6 /* Color.m in Sources */,
182 | 84C500D41CB4D24E00A2156C /* AppDelegate.m in Sources */,
183 | 84C500D11CB4D24E00A2156C /* main.m in Sources */,
184 | );
185 | runOnlyForDeploymentPostprocessing = 0;
186 | };
187 | /* End PBXSourcesBuildPhase section */
188 |
189 | /* Begin PBXVariantGroup section */
190 | 84C500D81CB4D24E00A2156C /* Main.storyboard */ = {
191 | isa = PBXVariantGroup;
192 | children = (
193 | 84C500D91CB4D24E00A2156C /* Base */,
194 | );
195 | name = Main.storyboard;
196 | sourceTree = "";
197 | };
198 | 84C500DD1CB4D24E00A2156C /* LaunchScreen.storyboard */ = {
199 | isa = PBXVariantGroup;
200 | children = (
201 | 84C500DE1CB4D24E00A2156C /* Base */,
202 | );
203 | name = LaunchScreen.storyboard;
204 | sourceTree = "";
205 | };
206 | /* End PBXVariantGroup section */
207 |
208 | /* Begin XCBuildConfiguration section */
209 | 84C500E11CB4D24E00A2156C /* Debug */ = {
210 | isa = XCBuildConfiguration;
211 | buildSettings = {
212 | ALWAYS_SEARCH_USER_PATHS = NO;
213 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
214 | CLANG_CXX_LIBRARY = "libc++";
215 | CLANG_ENABLE_MODULES = YES;
216 | CLANG_ENABLE_OBJC_ARC = YES;
217 | CLANG_WARN_BOOL_CONVERSION = YES;
218 | CLANG_WARN_CONSTANT_CONVERSION = YES;
219 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
220 | CLANG_WARN_EMPTY_BODY = YES;
221 | CLANG_WARN_ENUM_CONVERSION = YES;
222 | CLANG_WARN_INFINITE_RECURSION = YES;
223 | CLANG_WARN_INT_CONVERSION = YES;
224 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
225 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
226 | CLANG_WARN_UNREACHABLE_CODE = YES;
227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
228 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
229 | COPY_PHASE_STRIP = NO;
230 | DEBUG_INFORMATION_FORMAT = dwarf;
231 | ENABLE_STRICT_OBJC_MSGSEND = YES;
232 | ENABLE_TESTABILITY = YES;
233 | GCC_C_LANGUAGE_STANDARD = gnu99;
234 | GCC_DYNAMIC_NO_PIC = NO;
235 | GCC_NO_COMMON_BLOCKS = YES;
236 | GCC_OPTIMIZATION_LEVEL = 0;
237 | GCC_PREPROCESSOR_DEFINITIONS = (
238 | "DEBUG=1",
239 | "$(inherited)",
240 | );
241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
243 | GCC_WARN_UNDECLARED_SELECTOR = YES;
244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
245 | GCC_WARN_UNUSED_FUNCTION = YES;
246 | GCC_WARN_UNUSED_VARIABLE = YES;
247 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
248 | MTL_ENABLE_DEBUG_INFO = YES;
249 | ONLY_ACTIVE_ARCH = YES;
250 | SDKROOT = iphoneos;
251 | TARGETED_DEVICE_FAMILY = "1,2";
252 | };
253 | name = Debug;
254 | };
255 | 84C500E21CB4D24E00A2156C /* Release */ = {
256 | isa = XCBuildConfiguration;
257 | buildSettings = {
258 | ALWAYS_SEARCH_USER_PATHS = NO;
259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
260 | CLANG_CXX_LIBRARY = "libc++";
261 | CLANG_ENABLE_MODULES = YES;
262 | CLANG_ENABLE_OBJC_ARC = YES;
263 | CLANG_WARN_BOOL_CONVERSION = YES;
264 | CLANG_WARN_CONSTANT_CONVERSION = YES;
265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
266 | CLANG_WARN_EMPTY_BODY = YES;
267 | CLANG_WARN_ENUM_CONVERSION = YES;
268 | CLANG_WARN_INFINITE_RECURSION = YES;
269 | CLANG_WARN_INT_CONVERSION = YES;
270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
271 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
272 | CLANG_WARN_UNREACHABLE_CODE = YES;
273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
274 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
275 | COPY_PHASE_STRIP = NO;
276 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
277 | ENABLE_NS_ASSERTIONS = NO;
278 | ENABLE_STRICT_OBJC_MSGSEND = YES;
279 | GCC_C_LANGUAGE_STANDARD = gnu99;
280 | GCC_NO_COMMON_BLOCKS = YES;
281 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
282 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
283 | GCC_WARN_UNDECLARED_SELECTOR = YES;
284 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
285 | GCC_WARN_UNUSED_FUNCTION = YES;
286 | GCC_WARN_UNUSED_VARIABLE = YES;
287 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
288 | MTL_ENABLE_DEBUG_INFO = NO;
289 | SDKROOT = iphoneos;
290 | TARGETED_DEVICE_FAMILY = "1,2";
291 | VALIDATE_PRODUCT = YES;
292 | };
293 | name = Release;
294 | };
295 | 84C500E41CB4D24E00A2156C /* Debug */ = {
296 | isa = XCBuildConfiguration;
297 | buildSettings = {
298 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
299 | CODE_SIGN_IDENTITY = "iPhone Developer";
300 | INFOPLIST_FILE = iCards/Info.plist;
301 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
302 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
303 | PRODUCT_BUNDLE_IDENTIFIER = Ding.iCards;
304 | PRODUCT_NAME = "$(TARGET_NAME)";
305 | };
306 | name = Debug;
307 | };
308 | 84C500E51CB4D24E00A2156C /* Release */ = {
309 | isa = XCBuildConfiguration;
310 | buildSettings = {
311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
312 | CODE_SIGN_IDENTITY = "iPhone Developer";
313 | INFOPLIST_FILE = iCards/Info.plist;
314 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
315 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
316 | PRODUCT_BUNDLE_IDENTIFIER = Ding.iCards;
317 | PRODUCT_NAME = "$(TARGET_NAME)";
318 | };
319 | name = Release;
320 | };
321 | /* End XCBuildConfiguration section */
322 |
323 | /* Begin XCConfigurationList section */
324 | 84C500C71CB4D24E00A2156C /* Build configuration list for PBXProject "iCards" */ = {
325 | isa = XCConfigurationList;
326 | buildConfigurations = (
327 | 84C500E11CB4D24E00A2156C /* Debug */,
328 | 84C500E21CB4D24E00A2156C /* Release */,
329 | );
330 | defaultConfigurationIsVisible = 0;
331 | defaultConfigurationName = Release;
332 | };
333 | 84C500E31CB4D24E00A2156C /* Build configuration list for PBXNativeTarget "iCards" */ = {
334 | isa = XCConfigurationList;
335 | buildConfigurations = (
336 | 84C500E41CB4D24E00A2156C /* Debug */,
337 | 84C500E51CB4D24E00A2156C /* Release */,
338 | );
339 | defaultConfigurationIsVisible = 0;
340 | defaultConfigurationName = Release;
341 | };
342 | /* End XCConfigurationList section */
343 | };
344 | rootObject = 84C500C41CB4D24E00A2156C /* Project object */;
345 | }
346 |
--------------------------------------------------------------------------------
/iCards.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iCards.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrcoder/iCards/a29fc86d6085261cf4c033c5e83d23d00623ffb1/iCards.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iCards.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/iCards.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/iCards.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/iCards.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | iCards.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 84C500CB1CB4D24E00A2156C
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/iCards/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/iCards/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/iCards/Assets.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" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/iCards/Base.lproj/LaunchScreen.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 |
27 |
28 |
--------------------------------------------------------------------------------
/iCards/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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
70 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/iCards/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIRequiresFullScreen
34 |
35 | UIStatusBarHidden
36 |
37 | UISupportedInterfaceOrientations
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationLandscapeLeft
41 | UIInterfaceOrientationLandscapeRight
42 |
43 | UISupportedInterfaceOrientations~ipad
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationPortraitUpsideDown
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/iCards/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/iCards/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 |
10 | #import "ViewController.h"
11 | #import "Color.h"
12 | #import "iCards.h"
13 |
14 | @interface ViewController ()
15 |
16 | @property (weak, nonatomic) IBOutlet iCards *cards;
17 | @property (nonatomic, strong) NSMutableArray *cardsData;
18 |
19 | @end
20 |
21 | @implementation ViewController
22 |
23 | - (NSMutableArray *)cardsData {
24 | if (_cardsData == nil) {
25 | _cardsData = [NSMutableArray array];
26 | }
27 | return _cardsData;
28 | }
29 |
30 | - (void)viewDidLoad {
31 | [super viewDidLoad];
32 | [self makeCardsData];
33 | self.cards.dataSource = self;
34 | self.cards.delegate = self;
35 | }
36 | - (void)didReceiveMemoryWarning {
37 | [super didReceiveMemoryWarning];
38 | }
39 |
40 | - (void)makeCardsData {
41 | for (int i=0; i<100; i++) {
42 | [self.cardsData addObject:@(i)];
43 | }
44 | }
45 |
46 | - (IBAction)changeOffset:(UISegmentedControl *)sender {
47 | switch (sender.selectedSegmentIndex) {
48 | case 0:
49 | self.cards.offset = CGSizeMake(5, 5);
50 | break;
51 | case 1:
52 | self.cards.offset = CGSizeMake(0, 5);
53 | break;
54 | case 2:
55 | self.cards.offset = CGSizeMake(-5, 5);
56 | break;
57 | default:
58 | self.cards.offset = CGSizeMake(-5, -5);
59 | break;
60 | }
61 | }
62 | - (IBAction)changeVisibleNumbers:(UISegmentedControl *)sender {
63 | switch (sender.selectedSegmentIndex) {
64 | case 0:
65 | self.cards.numberOfVisibleItems = 3;
66 | break;
67 | case 1:
68 | self.cards.numberOfVisibleItems = 2;
69 | break;
70 | default:
71 | self.cards.numberOfVisibleItems = 5;
72 | break;
73 | }
74 | }
75 | - (IBAction)changeShowCyclicallyState:(UISwitch *)sender {
76 | self.cards.showedCyclically = sender.isOn;
77 | }
78 |
79 | #pragma mark - iCardsDataSource methods
80 |
81 | - (NSInteger)numberOfItemsInCards:(iCards *)cards {
82 | return self.cardsData.count;
83 | }
84 |
85 | - (UIView *)cards:(iCards *)cards viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view {
86 | UILabel *label = (UILabel *)view;
87 | if (label == nil) {
88 | CGSize size = cards.frame.size;
89 | CGRect labelFrame = CGRectMake(0, 0, size.width - 30, size.height - 20);
90 | label = [[UILabel alloc] initWithFrame:labelFrame];
91 | label.textAlignment = NSTextAlignmentCenter;
92 | label.layer.cornerRadius = 5;
93 | }
94 | label.text = [self.cardsData[index] stringValue];
95 | label.layer.backgroundColor = [Color randomColor].CGColor;
96 | return label;
97 | }
98 |
99 | #pragma mark - iCardsDelegate methods
100 |
101 | - (void)cards:(iCards *)cards beforeSwipingItemAtIndex:(NSInteger)index {
102 | NSLog(@"Begin swiping card %ld!", index);
103 | }
104 |
105 | - (void)cards:(iCards *)cards didLeftRemovedItemAtIndex:(NSInteger)index {
106 | NSLog(@"<--%ld", index);
107 | }
108 |
109 | - (void)cards:(iCards *)cards didRightRemovedItemAtIndex:(NSInteger)index {
110 | NSLog(@"%ld-->", index);
111 | }
112 |
113 | - (void)cards:(iCards *)cards didRemovedItemAtIndex:(NSInteger)index {
114 | NSLog(@"index of removed card: %ld", index);
115 | }
116 |
117 | @end
118 |
--------------------------------------------------------------------------------
/iCards/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/iCards.h:
--------------------------------------------------------------------------------
1 | //
2 | // iCards.h
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol iCardsDataSource, iCardsDelegate;
12 |
13 | @interface iCards : UIView
14 |
15 | @property (nonatomic, weak) id dataSource;
16 | @property (nonatomic, weak) id delegate;
17 |
18 | // Default is YES
19 | @property (nonatomic, assign) BOOL showedCyclically;
20 |
21 | // We will creat this number of views, so not too many; default is 3
22 | @property (nonatomic, assign) NSInteger numberOfVisibleItems;
23 |
24 | // Offset for the next card to the current card, (it will decide the cards appearance, the top card is on top-left, top, or bottom-right and so on; default is (5, 5)
25 | @property (nonatomic, assign) CGSize offset;
26 |
27 | // If there is only one card, maybe you don't want to swipe it
28 | @property (nonatomic, assign) BOOL swipeEnabled;
29 |
30 | // The first visible card on top
31 | @property (nonatomic, strong, readonly) UIView *topCard;
32 |
33 | /**
34 | * Refresh to show data source
35 | */
36 | - (void)reloadData;
37 |
38 | @end
39 |
40 | @protocol iCardsDataSource
41 | @required
42 |
43 | - (NSInteger)numberOfItemsInCards:(iCards *)cards;
44 | - (UIView *)cards:(iCards *)cards viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view;
45 |
46 | @end
47 |
48 | @protocol iCardsDelegate
49 | @optional
50 |
51 | - (void)cards:(iCards *)cards beforeSwipingItemAtIndex:(NSInteger)index;
52 | - (void)cards:(iCards *)cards didRemovedItemAtIndex:(NSInteger)index;
53 | - (void)cards:(iCards *)cards didLeftRemovedItemAtIndex:(NSInteger)index;
54 | - (void)cards:(iCards *)cards didRightRemovedItemAtIndex:(NSInteger)index;
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/src/iCards.m:
--------------------------------------------------------------------------------
1 | //
2 | // iCards.m
3 | // iCards
4 | //
5 | // Created by admin on 16/4/6.
6 | // Copyright © 2016年 Ding. All rights reserved.
7 | //
8 |
9 | #import "iCards.h"
10 |
11 | // distance from center where the action applies. Higher = swipe further in order for the action to be called
12 | static const CGFloat kActionMargin = 120;
13 | // how quickly the card shrinks. Higher = slower shrinking
14 | static const CGFloat kScaleStrength = 4;
15 | // upper bar for how much the card shrinks. Higher = shrinks less
16 | static const CGFloat kScaleMax = 0.93;
17 | // the maximum rotation allowed in radians. Higher = card can keep rotating longer
18 | static const CGFloat kRotationMax = 1.0;
19 | // strength of rotation. Higher = weaker rotation
20 | static const CGFloat kRotationStrength = 320;
21 | // Higher = stronger rotation angle
22 | static const CGFloat kRotationAngle = M_PI / 8;
23 |
24 | @interface iCards ()
25 |
26 | @property (strong, nonatomic) NSMutableArray *visibleViews;
27 | @property (strong, nonatomic) UIView *reusingView;
28 |
29 | @property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
30 | @property (nonatomic, assign) CGPoint originalPoint;
31 | @property (nonatomic, assign) CGFloat xFromCenter;
32 | @property (nonatomic, assign) CGFloat yFromCenter;
33 | @property (nonatomic, assign) NSInteger currentIndex;
34 | @property (nonatomic, assign) BOOL swipeEnded;
35 |
36 | @end
37 |
38 | @implementation iCards
39 |
40 | - (void)setUp {
41 | _showedCyclically = YES;
42 | _numberOfVisibleItems = 3;
43 | _offset = CGSizeMake(5, 5);
44 | _swipeEnded = YES;
45 | [self addGestureRecognizer:self.panGestureRecognizer];
46 | }
47 |
48 | - (instancetype)initWithCoder:(NSCoder *)coder {
49 | self = [super initWithCoder:coder];
50 | if (self) {
51 | [self setUp];
52 | }
53 | return self;
54 | }
55 |
56 | - (instancetype)initWithFrame:(CGRect)frame {
57 | self = [super initWithFrame:frame];
58 | if (self) {
59 | [self setUp];
60 | }
61 | return self;
62 | }
63 |
64 | #pragma mark - setters and getters
65 |
66 | - (void)setShowedCyclically:(BOOL)showedCyclically {
67 | _showedCyclically = showedCyclically;
68 | [self reloadData];
69 | }
70 | - (void)setOffset:(CGSize)offset {
71 | _offset = offset;
72 | [self reloadData];
73 | }
74 | - (void)setNumberOfVisibleItems:(NSInteger)numberOfVisibleItems {
75 | NSInteger cardsNumber = numberOfVisibleItems;
76 | if ([self.dataSource respondsToSelector:@selector(numberOfItemsInCards:)]) {
77 | cardsNumber = [self.dataSource numberOfItemsInCards:self];
78 | }
79 | if (cardsNumber >= numberOfVisibleItems) {
80 | _numberOfVisibleItems = numberOfVisibleItems;
81 | } else {
82 | _numberOfVisibleItems = cardsNumber;
83 | }
84 | [self reloadData];
85 | }
86 | - (void)setDataSource:(id)dataSource {
87 | _dataSource = dataSource;
88 | [self reloadData];
89 | }
90 | - (void)setSwipeEnabled:(BOOL)swipeEnabled {
91 | _swipeEnabled = swipeEnabled;
92 | self.panGestureRecognizer.enabled = swipeEnabled;
93 | }
94 | - (NSMutableArray *)visibleViews {
95 | if (_visibleViews == nil) {
96 | _visibleViews = [[NSMutableArray alloc] initWithCapacity:_numberOfVisibleItems];
97 | }
98 | return _visibleViews;
99 | }
100 | - (UIPanGestureRecognizer *)panGestureRecognizer {
101 | if (_panGestureRecognizer == nil) {
102 | _panGestureRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(dragAction:)];
103 | }
104 | return _panGestureRecognizer;
105 | }
106 | - (UIView *)topCard {
107 | return [self.visibleViews firstObject];
108 | }
109 |
110 | #pragma mark - main methods
111 |
112 | - (void)reloadData {
113 | _currentIndex = 0;
114 | _reusingView = nil;
115 | [self.visibleViews removeAllObjects];
116 | if ([self.dataSource respondsToSelector:@selector(numberOfItemsInCards:)]) {
117 | NSInteger totalNumber = [self.dataSource numberOfItemsInCards:self];
118 | if (totalNumber > 0) {
119 | if (totalNumber < _numberOfVisibleItems) {
120 | _numberOfVisibleItems = totalNumber;
121 | }
122 | if ([self.dataSource respondsToSelector:@selector(cards:viewForItemAtIndex:reusingView:)]) {
123 | for (NSInteger i=0; i<_numberOfVisibleItems; i++) {
124 | UIView *view = [self.dataSource cards:self viewForItemAtIndex:i reusingView:_reusingView];
125 | [self.visibleViews addObject:view];
126 | }
127 | }
128 | }
129 | }
130 | [self layoutCards];
131 | }
132 |
133 | - (void)layoutCards {
134 | NSInteger count = self.visibleViews.count;
135 | if (count <= 0) {
136 | return;
137 | }
138 |
139 | for (UIView *view in self.subviews) {
140 | [view removeFromSuperview];
141 | }
142 |
143 | [self layoutIfNeeded];
144 |
145 | CGFloat width = self.frame.size.width;
146 | CGFloat height = self.frame.size.height;
147 | CGFloat horizonOffset = _offset.width;
148 | CGFloat verticalOffset = _offset.height;
149 | UIView *lastCard = [self.visibleViews lastObject];
150 | CGFloat cardWidth = lastCard.frame.size.width;
151 | CGFloat cardHeight = lastCard.frame.size.height;
152 | CGFloat firstCardX = (width - cardWidth - (_numberOfVisibleItems - 1) * fabs(horizonOffset)) * 0.5;
153 | if (horizonOffset < 0) {
154 | firstCardX += (_numberOfVisibleItems - 1) * fabs(horizonOffset);
155 | }
156 | CGFloat firstCardY = (height - cardHeight - (_numberOfVisibleItems - 1) * fabs(verticalOffset)) * 0.5;
157 | if (verticalOffset < 0) {
158 | firstCardY += (_numberOfVisibleItems - 1) * fabs(verticalOffset);
159 | }
160 | [UIView animateWithDuration:0.08 animations:^{
161 | for (NSInteger i=0; i totalNumber - 1) {
177 | _currentIndex = 0;
178 | }
179 | if (self.swipeEnded) {
180 | self.swipeEnded = NO;
181 | if ([self.delegate respondsToSelector:@selector(cards:beforeSwipingItemAtIndex:)]) {
182 | [self.delegate cards:self beforeSwipingItemAtIndex:_currentIndex];
183 | }
184 | }
185 | UIView *firstCard = [self.visibleViews firstObject];
186 | self.xFromCenter = [gestureRecognizer translationInView:firstCard].x; // positive for right swipe, negative for left
187 | self.yFromCenter = [gestureRecognizer translationInView:firstCard].y; // positive for up, negative for down
188 | switch (gestureRecognizer.state) {
189 |
190 | case UIGestureRecognizerStateBegan: {
191 | self.originalPoint = firstCard.center;
192 | break;
193 | };
194 | case UIGestureRecognizerStateChanged:{
195 | CGFloat rotationStrength = MIN(self.xFromCenter / kRotationStrength, kRotationMax);
196 | CGFloat rotationAngel = (CGFloat) (kRotationAngle * rotationStrength);
197 | CGFloat scale = MAX(1 - fabs(rotationStrength) / kScaleStrength, kScaleMax);
198 | firstCard.center = CGPointMake(self.originalPoint.x + self.xFromCenter, self.originalPoint.y + self.yFromCenter);
199 | CGAffineTransform transform = CGAffineTransformMakeRotation(rotationAngel);
200 | CGAffineTransform scaleTransform = CGAffineTransformScale(transform, scale, scale);
201 | firstCard.transform = scaleTransform;
202 | break;
203 | };
204 | case UIGestureRecognizerStateEnded: {
205 | [self afterSwipedCard:firstCard];
206 | break;
207 | };
208 | default:
209 | break;
210 | }
211 | }
212 | - (void)afterSwipedCard:(UIView *)card {
213 | if (self.xFromCenter > kActionMargin) {
214 | [self rightActionForCard:card];
215 | } else if (self.xFromCenter < -kActionMargin) {
216 | [self leftActionForCard:card];
217 | } else {
218 | self.swipeEnded = YES;
219 | [UIView animateWithDuration:0.3
220 | animations: ^{
221 | card.center = self.originalPoint;
222 | card.transform = CGAffineTransformMakeRotation(0);
223 | }];
224 | }
225 | }
226 | -(void)rightActionForCard:(UIView *)card {
227 | CGPoint finishPoint = CGPointMake(500, 2 * self.yFromCenter + self.originalPoint.y);
228 | [UIView animateWithDuration:0.3
229 | animations: ^{
230 | card.center = finishPoint;
231 | } completion: ^(BOOL complete) {
232 | if ([self.delegate respondsToSelector:@selector(cards:didRightRemovedItemAtIndex:)]) {
233 | [self.delegate cards:self didRightRemovedItemAtIndex:_currentIndex];
234 | }
235 | [self cardSwipedAction:card];
236 | }];
237 |
238 | }
239 |
240 | -(void)leftActionForCard:(UIView *)card {
241 | CGPoint finishPoint = CGPointMake(-500, 2 * self.yFromCenter + self.originalPoint.y);
242 | [UIView animateWithDuration:0.3
243 | animations:^{
244 | card.center = finishPoint;
245 | } completion:^(BOOL complete) {
246 | if ([self.delegate respondsToSelector:@selector(cards:didLeftRemovedItemAtIndex:)]) {
247 | [self.delegate cards:self didLeftRemovedItemAtIndex:_currentIndex];
248 | }
249 | [self cardSwipedAction:card];
250 | }];
251 | }
252 |
253 | - (void)cardSwipedAction:(UIView *)card {
254 | self.swipeEnded = YES;
255 | card.transform = CGAffineTransformMakeRotation(0);
256 | card.center = self.originalPoint;
257 | CGRect cardFrame = card.frame;
258 | _reusingView = card;
259 | [self.visibleViews removeObject:card];
260 | [card removeFromSuperview];
261 |
262 | NSInteger totalNumber = [self.dataSource numberOfItemsInCards:self];
263 | UIView *newCard;
264 | NSInteger newIndex = _currentIndex + _numberOfVisibleItems;
265 | if (newIndex < totalNumber) {
266 | newCard = [self.dataSource cards:self viewForItemAtIndex:newIndex reusingView:_reusingView];
267 | } else {
268 | if (_showedCyclically) {
269 | if (totalNumber == 1) {
270 | newIndex = 0;
271 | } else {
272 | newIndex %= totalNumber;
273 | }
274 | newCard = [self.dataSource cards:self viewForItemAtIndex:newIndex reusingView:_reusingView];
275 | }
276 | }
277 | if (newCard) {
278 | newCard.frame = cardFrame;
279 | [self.visibleViews addObject:newCard];
280 | }
281 |
282 | if ([self.delegate respondsToSelector:@selector(cards:didRemovedItemAtIndex:)]) {
283 | [self.delegate cards:self didRemovedItemAtIndex:_currentIndex];
284 | }
285 | _currentIndex ++;
286 | [self layoutCards];
287 | }
288 |
289 | @end
290 |
--------------------------------------------------------------------------------