├── surfingRefreshControlDemo
├── surfingRefreshControlDemo
│ ├── surfing.jpg
│ ├── SurfingRefreshControl
│ │ ├── Plists
│ │ │ ├── square.plist
│ │ │ ├── polygon.plist
│ │ │ ├── storehouserefresh.plist
│ │ │ └── surfing.plist
│ │ ├── SurfingBarItem.swift
│ │ └── SurfingRefreshControl.swift
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── DemoViewController.swift
│ └── Base.lproj
│ │ └── LaunchScreen.storyboard
└── surfingRefreshControlDemo.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ ├── peiwei.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── antonioalves.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ ├── xcuserdata
│ ├── antonioalves.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ ├── xcschememanagement.plist
│ │ │ └── surfingRefreshControlDemo.xcscheme
│ └── peiwei.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── surfingRefreshControlDemo.xcscheme
│ └── project.pbxproj
├── SurfingRefreshControl
├── Plists
│ ├── square.plist
│ ├── polygon.plist
│ ├── storehouserefresh.plist
│ └── surfing.plist
├── SurfingBarItem.swift
└── SurfingRefreshControl.swift
├── SurfingRefreshControl.podspec
├── LICENSE
└── README.md
/surfingRefreshControlDemo/surfingRefreshControlDemo/surfing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peiweichen/SurfingRefreshControl/HEAD/surfingRefreshControlDemo/surfingRefreshControlDemo/surfing.jpg
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/project.xcworkspace/xcuserdata/peiwei.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peiweichen/SurfingRefreshControl/HEAD/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/project.xcworkspace/xcuserdata/peiwei.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/project.xcworkspace/xcuserdata/antonioalves.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peiweichen/SurfingRefreshControl/HEAD/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/project.xcworkspace/xcuserdata/antonioalves.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/SurfingRefreshControl/Plists/square.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {0,0}
8 | {30,0}
9 | {30,30}
10 | {0,30}
11 |
12 | endPoints
13 |
14 | {30,0}
15 | {30,30}
16 | {0,30}
17 | {0,0}
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/SurfingRefreshControl/Plists/square.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {0,0}
8 | {30,0}
9 | {30,30}
10 | {0,30}
11 |
12 | endPoints
13 |
14 | {30,0}
15 | {30,30}
16 | {0,30}
17 | {0,0}
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/xcuserdata/antonioalves.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/SurfingRefreshControl/Plists/polygon.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {16.5,1}
8 | {32.19,10.67}
9 | {26.2,26.33}
10 | {6.8,26.33}
11 | {0.81,10.67}
12 |
13 | endPoints
14 |
15 | {32.19,10.67}
16 | {26.2,26.33}
17 | {6.8,26.33}
18 | {0.81,10.67}
19 | {16.5,1}
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/xcuserdata/peiwei.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | surfingRefreshControlDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | F4EEEF321D0A3D5C00FA3BB4
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/xcuserdata/antonioalves.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | surfingRefreshControlDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | F4EEEF321D0A3D5C00FA3BB4
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/SurfingRefreshControl/Plists/polygon.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {16.5,1}
8 | {32.19,10.67}
9 | {26.2,26.33}
10 | {6.8,26.33}
11 | {0.81,10.67}
12 |
13 | endPoints
14 |
15 | {32.19,10.67}
16 | {26.2,26.33}
17 | {6.8,26.33}
18 | {0.81,10.67}
19 | {16.5,1}
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // surfingRefreshControlDemo
4 | //
5 | // Created by chenpeiwei on 6/10/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | self.window = UIWindow(frame: UIScreen.main.bounds)
19 | self.window?.rootViewController = DemoViewController()
20 | self.window?.makeKeyAndVisible()
21 | return true
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/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 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/SurfingRefreshControl.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "SurfingRefreshControl"
3 | s.version = "1.0"
4 | s.license = { :type => "MIT", :file => "LICENSE" }
5 | s.summary = "Customizable pull-to-refresh control,written in pure Swift"
6 | s.homepage = "https://github.com/peiweichen/SurfingRefreshControl"
7 | s.author = { "Peiwei" => "peiwei233@gmail.com" }
8 | s.source = { :git => "https://github.com/peiweichen/SurfingRefreshControl.git", :tag => s.version }
9 | s.source_files = 'SurfingRefreshControl'
10 | s.description = <<-DESC
11 | This project is heavily inspired by CBStoreHouseRefreshControl which is Objective-C implemented. SurfingRefreshControl provides you a chance to use pure Swift alternative in your next app.
12 | DESC
13 | s.platform = :ios, '7.0'
14 | s.screenshots = "http://peiweichen.github.io/outofwebsite/gif/surfing.gif", "http://peiweichen.github.io/outofwebsite/gif/storehouse.gif"
15 | s.requires_arc = true
16 | end
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Peiwei
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 |
--------------------------------------------------------------------------------
/SurfingRefreshControl/Plists/storehouserefresh.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {0,35}
8 | {12,42}
9 | {24,35}
10 | {0,35}
11 | {0,21}
12 | {12,28}
13 | {24,35}
14 | {24,21}
15 | {0,21}
16 | {0,21}
17 | {12,14}
18 | {12,14}
19 | {24,7}
20 | {0,7}
21 |
22 | endPoints
23 |
24 | {12,42}
25 | {24,35}
26 | {12,28}
27 | {12,28}
28 | {12,28}
29 | {24,21}
30 | {24,21}
31 | {12,14}
32 | {12,14}
33 | {0,7}
34 | {0,7}
35 | {24,7}
36 | {12,0}
37 | {12,0}
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/SurfingRefreshControl/Plists/storehouserefresh.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {0,35}
8 | {12,42}
9 | {24,35}
10 | {0,35}
11 | {0,21}
12 | {12,28}
13 | {24,35}
14 | {24,21}
15 | {0,21}
16 | {0,21}
17 | {12,14}
18 | {12,14}
19 | {24,7}
20 | {0,7}
21 |
22 | endPoints
23 |
24 | {12,42}
25 | {24,35}
26 | {12,28}
27 | {12,28}
28 | {12,28}
29 | {24,21}
30 | {24,21}
31 | {12,14}
32 | {12,14}
33 | {0,7}
34 | {0,7}
35 | {24,7}
36 | {12,0}
37 | {12,0}
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/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 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UIRequiresFullScreen
32 |
33 | UIStatusBarHidden
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/SurfingRefreshControl/SurfingBarItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SurfBarItem.swift
3 | // CBStoreHouseRefreshControl
4 | //
5 | // Created by chenpeiwei on 6/8/16.
6 | // Copyright © 2016 Peiwei Chen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SurfingBarItem: UIView {
12 | var translationX:CGFloat = 0
13 | fileprivate var lineWidth:CGFloat = 0
14 | fileprivate var startPoint:CGPoint = CGPoint.zero
15 | fileprivate var endPoint:CGPoint = CGPoint.zero
16 | fileprivate var middlePoint:CGPoint = CGPoint.zero
17 | fileprivate var color:UIColor = UIColor.white
18 |
19 | convenience init(frame:CGRect,startPoint:CGPoint,endPoint:CGPoint,color:UIColor,lineWidth:CGFloat) {
20 | self.init(frame: frame)
21 | self.startPoint = startPoint
22 | self.endPoint = endPoint
23 | self.color = color
24 | self.lineWidth = lineWidth
25 | self.middlePoint = CGPoint(x: (startPoint.x+endPoint.x)*0.5, y:( startPoint.y+endPoint.y)*0.5)
26 | }
27 |
28 | func setHorizontalRandomness(_ horizontalRandomness:Int,dropHeight:CGFloat) {
29 | let horizontalRandomnessCGFloat = CGFloat(horizontalRandomness)
30 | let ramdomNumberInt = Int(arc4random_uniform(65535))
31 | let randomRangeNumberCGFloat = CGFloat( ramdomNumberInt%horizontalRandomness*2 )
32 | self.translationX = -horizontalRandomnessCGFloat + randomRangeNumberCGFloat
33 | }
34 |
35 | override func draw(_ rect: CGRect) {
36 | let bezierPath = UIBezierPath()
37 | bezierPath.move(to: self.startPoint)
38 | bezierPath.addLine(to: self.endPoint)
39 | self.color.setStroke()
40 | bezierPath.lineWidth = self.lineWidth
41 | bezierPath.stroke()
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/SurfingRefreshControl/SurfingBarItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SurfBarItem.swift
3 | // CBStoreHouseRefreshControl
4 | //
5 | // Created by chenpeiwei on 6/8/16.
6 | // Copyright © 2016 Peiwei Chen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SurfingBarItem: UIView {
12 | var translationX:CGFloat = 0
13 | fileprivate var lineWidth:CGFloat = 0
14 | fileprivate var startPoint:CGPoint = CGPoint.zero
15 | fileprivate var endPoint:CGPoint = CGPoint.zero
16 | fileprivate var middlePoint:CGPoint = CGPoint.zero
17 | fileprivate var color:UIColor = UIColor.white
18 |
19 | convenience init(frame:CGRect,startPoint:CGPoint,endPoint:CGPoint,color:UIColor,lineWidth:CGFloat) {
20 | self.init(frame: frame)
21 | self.startPoint = startPoint
22 | self.endPoint = endPoint
23 | self.color = color
24 | self.lineWidth = lineWidth
25 | self.middlePoint = CGPoint(x: (startPoint.x+endPoint.x)*0.5, y:( startPoint.y+endPoint.y)*0.5)
26 | }
27 |
28 | func setHorizontalRandomness(_ horizontalRandomness:Int,dropHeight:CGFloat) {
29 | let horizontalRandomnessCGFloat = CGFloat(horizontalRandomness)
30 | let ramdomNumberInt = Int(arc4random_uniform(65535))
31 | let randomRangeNumberCGFloat = CGFloat( ramdomNumberInt%horizontalRandomness*2 )
32 | self.translationX = -horizontalRandomnessCGFloat + randomRangeNumberCGFloat
33 | }
34 |
35 | override func draw(_ rect: CGRect) {
36 | let bezierPath = UIBezierPath()
37 | bezierPath.move(to: self.startPoint)
38 | bezierPath.addLine(to: self.endPoint)
39 | self.color.setStroke()
40 | bezierPath.lineWidth = self.lineWidth
41 | bezierPath.stroke()
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/SurfingRefreshControl/Plists/surfing.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {20.5,11.5}
8 | {8.5,11.5}
9 | {8.5,30.5}
10 | {21.5,29.5}
11 | {21.5,45.5}
12 | {32.5,10.5}
13 | {32.5,51.5}
14 | {47.5,51.5}
15 | {52.5,45.5}
16 | {65.5,10.5}
17 | {65.5,53.5}
18 | {66.5,9.5}
19 | {87.5,9.5}
20 | {87.5,29.5}
21 | {66.5,29.5}
22 | {87.5,50.5}
23 | {101.5,8.5}
24 | {121.5,8.5}
25 | {102.5,8.5}
26 | {105.06,27.73}
27 | {106.5,30.5}
28 | {135.5,7.5}
29 | {149.5,8.5}
30 | {149.5,51.5}
31 | {150.5,8.5}
32 | {171.5,49.5}
33 | {217.5,8.5}
34 | {191.5,8.5}
35 | {191.5,51.5}
36 | {217.5,51.5}
37 |
38 | endPoints
39 |
40 | {8.5,11.5}
41 | {8.5,30.5}
42 | {20.5,30.5}
43 | {21.5,45.5}
44 | {10.5,51.5}
45 | {32.5,51.5}
46 | {47.5,51.5}
47 | {52.5,45.5}
48 | {52.5,10.5}
49 | {65.5,53.5}
50 | {65.5,10.5}
51 | {87.5,9.5}
52 | {87.5,29.5}
53 | {66.5,29.5}
54 | {87.5,50.5}
55 | {95.5,50.5}
56 | {121.5,8.5}
57 | {101.5,8.5}
58 | {105.06,27.73}
59 | {108.5,53.5}
60 | {125.5,30.5}
61 | {135.5,51.5}
62 | {149.5,51.5}
63 | {149.5,8.5}
64 | {171.5,49.5}
65 | {177.5,8.5}
66 | {191.5,8.5}
67 | {191.5,51.5}
68 | {217.5,51.5}
69 | {217.5,29.5}
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/SurfingRefreshControl/Plists/surfing.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startPoints
6 |
7 | {20.5,11.5}
8 | {8.5,11.5}
9 | {8.5,30.5}
10 | {21.5,29.5}
11 | {21.5,45.5}
12 | {32.5,10.5}
13 | {32.5,51.5}
14 | {47.5,51.5}
15 | {52.5,45.5}
16 | {65.5,10.5}
17 | {65.5,53.5}
18 | {66.5,9.5}
19 | {87.5,9.5}
20 | {87.5,29.5}
21 | {66.5,29.5}
22 | {87.5,50.5}
23 | {101.5,8.5}
24 | {121.5,8.5}
25 | {102.5,8.5}
26 | {105.06,27.73}
27 | {106.5,30.5}
28 | {135.5,7.5}
29 | {149.5,8.5}
30 | {149.5,51.5}
31 | {150.5,8.5}
32 | {171.5,49.5}
33 | {217.5,8.5}
34 | {191.5,8.5}
35 | {191.5,51.5}
36 | {217.5,51.5}
37 |
38 | endPoints
39 |
40 | {8.5,11.5}
41 | {8.5,30.5}
42 | {20.5,30.5}
43 | {21.5,45.5}
44 | {10.5,51.5}
45 | {32.5,51.5}
46 | {47.5,51.5}
47 | {52.5,45.5}
48 | {52.5,10.5}
49 | {65.5,53.5}
50 | {65.5,10.5}
51 | {87.5,9.5}
52 | {87.5,29.5}
53 | {66.5,29.5}
54 | {87.5,50.5}
55 | {95.5,50.5}
56 | {121.5,8.5}
57 | {101.5,8.5}
58 | {105.06,27.73}
59 | {108.5,53.5}
60 | {125.5,30.5}
61 | {135.5,51.5}
62 | {149.5,51.5}
63 | {149.5,8.5}
64 | {171.5,49.5}
65 | {177.5,8.5}
66 | {191.5,8.5}
67 | {191.5,51.5}
68 | {217.5,51.5}
69 | {217.5,29.5}
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # What is it
2 |
3 |
4 | This project is heavily inspired by [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl) which is Objective-C implemented. SurfingRefreshControl provides you a chance to use pure Swift alternative in your next app.
5 |
6 | You can customize any desired shape through `plist` file like below two shapes:
7 |
8 | 
9 |
10 | 
11 |
12 |
13 |
14 |
15 | # How to use
16 |
17 | ### Warning:Check out demo app directly if you're too lazy to read
18 |
19 | You can attach it to any `UIScrollView` ( like `UITableView` `UICollectionView`) :
20 |
21 | ```swift
22 | class func attachToScrollView(
23 | scrollView:UIScrollView,
24 | target:AnyObject,
25 | refreshAction:Selector,
26 | plist:String,
27 | color:UIColor=UIColor.blackColor(),
28 | lineWidth:CGFloat=2,
29 | dropHeight:CGFloat=80,
30 | scale:CGFloat=1,
31 | horizontalRandomness:Int=150,
32 | reverseLoadingAnimation:Bool=false,
33 | internalAnimationFactor:CGFloat=1.0) -> SurfingRefreshControl
34 | ```
35 |
36 | For instance:
37 |
38 | ```swift
39 | self.surfingRefreshControl = SurfingRefreshControl.attachToScrollView(tableView, target: self,refreshAction:#selector(DemoViewController.refreshTriggered), plist: "surfing", color: UIColor.whiteColor(),lineWidth: 1.5, dropHeight: 120, scale: 1.0, horizontalRandomness: 100, reverseLoadingAnimation: false, internalAnimationFactor: 0.8)
40 | ```
41 |
42 | Implement `UIScrollViewDelegate` in your `UIViewController` to notify SurfingRefreshControl your scrollView's movements:
43 |
44 | ```swift
45 | override func scrollViewDidScroll(scrollView: UIScrollView) {
46 | self.surfingRefreshControl.scrollViewDidScroll(scrollView)
47 | }
48 |
49 | override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
50 | self.surfingRefreshControl.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate)
51 | }
52 | ```
53 |
54 | When you're done with surfing,simply call
55 |
56 | ```swift
57 | self.surfingRefreshControl.finishLoading()
58 | ```
59 |
60 | # How to customize your refresh:
61 |
62 | SurfingRefreshControl make use of `SurfingBarItem` which actually is a UIView subclass, made of start point and end point to form a line.Imagine a squre need 4 line,thus need 4 SurfingBarItem,which equal to 4 start points and 4 end points.In conclusion,a square surfing refresh control would need a plist file of 4 start points and 4 end points,as shown below:
63 |
64 | 
65 |
66 |
67 | Square SurfingRefreshControl output is:
68 |
69 | 
70 |
71 |
72 | [PaintCode](http://www.paintcodeapp.com/) to generate your startpoints and endpoints without too much hassle:
73 |
74 | 
75 |
76 |
77 | Author
78 | ------
79 | Peiwei Chen
80 | peiwei233@gmail.com
81 | [Blog](http://peiweichen.github.io)
82 |
83 |
84 | License
85 | -------
86 | Copyright (c) 2016 Peiwei Chen . See the LICENSE file for license rights and limitations (MIT).
87 |
88 |
89 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/DemoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoViewController.swift
3 | // surfingRefreshControlDemo
4 | //
5 | // Created by chenpeiwei on 6/10/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | class DemoViewController: UITableViewController {
12 |
13 | var surfingRefreshControl:SurfingRefreshControl!
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 |
18 | self.title = "Surfing"
19 | self.navigationController?.navigationBar.tintColor = UIColor.white
20 | self.tableView.backgroundColor = UIColor.black
21 | self.tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cellIdentifier")
22 | self.tableView.tableFooterView = UIView()
23 | self.tableView.separatorStyle = .none
24 |
25 | //storehouserefresh,surfing,polygon,square
26 | surfingRefreshControl = SurfingRefreshControl.attachToScrollView(tableView, target: self, refreshAction: #selector(DemoViewController.refreshTriggered), plist: "surfing", color: UIColor.white, lineWidth: 2.0, dropHeight: 120, scale: 1.1, horizontalRandomness: 100, reverseLoadingAnimation: false, internalAnimationFactor: 0.8)
27 | }
28 | func refreshTriggered() {
29 | self.perform(#selector(DemoViewController.endRefresh), with: nil, afterDelay: 3.0, inModes: [RunLoopMode.commonModes])
30 | }
31 | func endRefresh() {
32 | surfingRefreshControl.finishLoading()
33 | }
34 | override func scrollViewDidScroll(_ scrollView: UIScrollView) {
35 | surfingRefreshControl.scrollViewDidScroll(scrollView)
36 | }
37 | override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
38 | surfingRefreshControl.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate)
39 | }
40 |
41 |
42 |
43 |
44 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
45 |
46 | return 1
47 | }
48 |
49 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
50 | let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")
51 | cell?.selectionStyle = .none
52 | cell?.backgroundColor = UIColor.clear
53 |
54 | let imageView = UIImageView(image: UIImage(named: "surfing.jpg"))
55 | imageView.translatesAutoresizingMaskIntoConstraints = false
56 | imageView.contentMode = .scaleAspectFill
57 | imageView.clipsToBounds = true
58 |
59 | cell?.contentView.addSubview(imageView)
60 |
61 | cell?.contentView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .top, relatedBy: .equal, toItem: cell?.contentView, attribute: .top, multiplier: 1, constant: 0))
62 | cell?.contentView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: cell?.contentView, attribute: .leading, multiplier: 1, constant: 0))
63 | cell?.contentView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .trailing, relatedBy: .equal, toItem: cell?.contentView, attribute: .trailing, multiplier: 1, constant: 0))
64 | cell?.contentView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .bottom, relatedBy: .equal, toItem: cell?.contentView, attribute: .bottom, multiplier: 1, constant: 0))
65 |
66 |
67 | return cell!
68 |
69 | }
70 |
71 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
72 | return 300
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/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 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/xcuserdata/peiwei.xcuserdatad/xcschemes/surfingRefreshControlDemo.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 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/xcuserdata/antonioalves.xcuserdatad/xcschemes/surfingRefreshControlDemo.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 |
--------------------------------------------------------------------------------
/SurfingRefreshControl/SurfingRefreshControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SurfingRefreshControl.swift
3 | // CBStoreHouseRefreshControl
4 | //
5 | // Created by chenpeiwei on 6/8/16.
6 | // Copyright © 2016 Peiwei Chen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | fileprivate func < (lhs: T?, rhs: T?) -> Bool {
11 | switch (lhs, rhs) {
12 | case let (l?, r?):
13 | return l < r
14 | case (nil, _?):
15 | return true
16 | default:
17 | return false
18 | }
19 | }
20 |
21 | fileprivate func >= (lhs: T?, rhs: T?) -> Bool {
22 | switch (lhs, rhs) {
23 | case let (l?, r?):
24 | return l >= r
25 | default:
26 | return !(lhs < rhs)
27 | }
28 | }
29 |
30 | fileprivate func <= (lhs: T?, rhs: T?) -> Bool {
31 | switch (lhs, rhs) {
32 | case let (l?, r?):
33 | return l <= r
34 | default:
35 | return !(rhs < lhs)
36 | }
37 | }
38 |
39 | struct SurfingConfigs {
40 | static let loadingIndividualAnimationTiming = 0.8
41 | static let barDarkAlpha:CGFloat = 0.4
42 | static let loadingTimingOffset = 0.1
43 | static let disappearDuration = 1.2
44 | static let relativeHeightFactor:CGFloat = 0.4
45 | static let startPointKey = "startPoints"
46 | static let endPointKey = "endPoints"
47 | static let xKey = "x"
48 | static let yKey = "y"
49 | }
50 |
51 | enum SurfingRefreshControlState {
52 | case idle
53 | case refreshing
54 | case disappearing
55 | }
56 |
57 | class SurfingRefreshControl: UIView {
58 | weak var scrollView:UIScrollView!
59 | var state:SurfingRefreshControlState = .idle
60 | var barItems:[SurfingBarItem]!
61 | var displayLink:CADisplayLink?
62 | var target:AnyObject!
63 | var action:Selector!
64 |
65 | var dropHeight:CGFloat!
66 | var disappearProgress:CGFloat!
67 | var internalAnimationFactor:CGFloat!
68 | var horizontalRandomness:Int!
69 | var reverseLoadingAnimation:Bool!
70 |
71 | var realContentOffsetY:CGFloat {
72 | get {
73 | return self.scrollView.contentOffset.y + self.scrollView.contentInset.top
74 | }
75 | }
76 |
77 | var surfingBarItemsAnimationProgress:CGFloat {
78 | get {
79 | return min(1.0,max(0,fabs(self.realContentOffsetY/self.dropHeight)))
80 | }
81 | }
82 |
83 | class func attachToScrollView(_ scrollView:UIScrollView,target:AnyObject,refreshAction:Selector,plist:String,color:UIColor=UIColor.black,lineWidth:CGFloat=2,dropHeight:CGFloat=80,scale:CGFloat=1,horizontalRandomness:Int=150,reverseLoadingAnimation:Bool=false,internalAnimationFactor:CGFloat=1.0) -> SurfingRefreshControl{
84 |
85 | let refreshControl = SurfingRefreshControl()
86 | refreshControl.dropHeight = dropHeight
87 | refreshControl.horizontalRandomness = horizontalRandomness
88 | refreshControl.scrollView = scrollView
89 | refreshControl.target = target
90 | refreshControl.action = refreshAction
91 | refreshControl.reverseLoadingAnimation = reverseLoadingAnimation
92 | refreshControl.internalAnimationFactor = internalAnimationFactor
93 | scrollView.addSubview(refreshControl)
94 |
95 | var width:CGFloat = 0
96 | var height:CGFloat = 0
97 |
98 | let dataDictionary = NSDictionary(contentsOfFile: Bundle.main.path(forResource: plist, ofType: "plist")!)
99 | let startPoints = dataDictionary?.object(forKey: SurfingConfigs.startPointKey) as! NSArray
100 | let endPoints = dataDictionary?.object(forKey: SurfingConfigs.endPointKey) as! NSArray
101 |
102 | for (index,startPointObject) in startPoints.enumerated() {
103 | let startPoint = CGPointFromString(startPointObject as! String)
104 | let endPoint = CGPointFromString(endPoints[index] as! String)
105 |
106 | if startPoint.x > width {
107 | width = startPoint.x
108 | }
109 | if endPoint.x > width {
110 | width = endPoint.x
111 | }
112 | if startPoint.y > height {
113 | height = startPoint.y
114 | }
115 | if endPoint.y > height {
116 | height = endPoint.y
117 | }
118 | }
119 |
120 | refreshControl.frame = CGRect(x: 0, y: 0, width: width, height: height)
121 |
122 | var surfingBarItems = [SurfingBarItem]()
123 |
124 | for (index,startPointObject) in startPoints.enumerated() {
125 | let startPoint = CGPointFromString(startPointObject as! String)
126 | let endPoint = CGPointFromString(endPoints[index] as! String)
127 | let surfingBarItem = SurfingBarItem(frame: refreshControl.frame, startPoint: startPoint, endPoint: endPoint, color: color, lineWidth: lineWidth)
128 | surfingBarItem.tag = index
129 | surfingBarItem.backgroundColor = UIColor.clear
130 | surfingBarItem.alpha = 0
131 | surfingBarItem.setHorizontalRandomness(refreshControl.horizontalRandomness, dropHeight: refreshControl.dropHeight)
132 | surfingBarItems.append(surfingBarItem)
133 | refreshControl.addSubview(surfingBarItem)
134 | }
135 | refreshControl.barItems = surfingBarItems
136 | refreshControl.transform = CGAffineTransform(scaleX: scale, y: scale)
137 |
138 | return refreshControl
139 | }
140 |
141 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
142 |
143 | if scrollView != self.scrollView {
144 | return
145 | }
146 |
147 | self.center = CGPoint(x: scrollView.frame.size.width*0.5, y: self.scrollView.contentOffset.y*SurfingConfigs.relativeHeightFactor)
148 | if self.state == SurfingRefreshControlState.idle {
149 | self.updateBarItemsWithProgress(self.surfingBarItemsAnimationProgress)
150 | }
151 | }
152 |
153 |
154 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
155 | if self.state == .idle && self.realContentOffsetY < -self.dropHeight {
156 | if self.surfingBarItemsAnimationProgress == 1 {
157 | self.state = .refreshing
158 | var newInsets = self.scrollView.contentInset
159 | newInsets.top += self.dropHeight
160 | let contentOffset = self.scrollView.contentOffset
161 |
162 | UIView.animate(withDuration: 0, animations: {
163 | self.scrollView.contentInset = newInsets
164 | self.scrollView.contentOffset = contentOffset
165 | })
166 |
167 | if self.target.responds(to: self.action) {
168 | self.target.perform(self.action, with: self)
169 | }
170 | self.startLoadingAnimation()
171 | }
172 | }
173 | }
174 |
175 | func updateBarItemsWithProgress(_ progress:CGFloat) {
176 | for (index,barItem) in self.barItems.enumerated() {
177 |
178 | //internalAnimationFactor: 1 means normal animation, 0.7 cut the animation to 70% compared to normal animation, in this case , we could handle 1-0.7 = 30%.
179 |
180 |
181 | let startPadding = (1 - self.internalAnimationFactor)/CGFloat(self.barItems.count)*CGFloat(index)
182 | let endPadding = 1 - self.internalAnimationFactor - startPadding
183 |
184 | if (progress == 1 || progress >= 1 - endPadding) {
185 | barItem.transform = CGAffineTransform.identity
186 | barItem.alpha = SurfingConfigs.barDarkAlpha
187 | } else if progress == 0 {
188 | barItem.setHorizontalRandomness(self.horizontalRandomness, dropHeight: self.dropHeight)
189 | }
190 | else {
191 |
192 | var surfingProgress:CGFloat!
193 | if progress <= startPadding {
194 | //key for internalAnimationFactor
195 | surfingProgress = 0
196 | } else {
197 | surfingProgress = min(1, (progress - startPadding)/self.internalAnimationFactor)
198 | }
199 | barItem.transform = CGAffineTransform(translationX: barItem.translationX*(1-surfingProgress), y: -self.dropHeight*(1-surfingProgress))
200 | barItem.transform = barItem.transform.rotated(by: CGFloat(M_PI)*surfingProgress)
201 | barItem.transform = barItem.transform.scaledBy(x: surfingProgress, y: surfingProgress)
202 | barItem.alpha = surfingProgress * SurfingConfigs.barDarkAlpha
203 | }
204 | }
205 | }
206 |
207 | func startLoadingAnimation() {
208 |
209 | //NSRunLoopCommonModes make it possible that animating even when user's dragging scrollView
210 | if self.reverseLoadingAnimation == true {
211 | for (index,barItem) in self.barItems.reversed().enumerated() {
212 | self.perform(#selector(SurfingRefreshControl.barItemAnimation(_:)), with: barItem, afterDelay: Double(index) * SurfingConfigs.loadingTimingOffset, inModes: [RunLoopMode.commonModes])
213 | }
214 | } else {
215 | for (index,barItem) in self.barItems.enumerated() {
216 | self.perform(#selector(SurfingRefreshControl.barItemAnimation(_:)), with: barItem, afterDelay: Double(index) * SurfingConfigs.loadingTimingOffset, inModes: [RunLoopMode.commonModes])
217 | }
218 | }
219 | }
220 |
221 | func finishLoading() {
222 | self.state = .disappearing
223 | self.disappearProgress = 0
224 | var newInset = self.scrollView.contentInset
225 | newInset.top = self.scrollView.contentInset.top - self.dropHeight
226 | UIView.animate(withDuration: SurfingConfigs.disappearDuration,animations: {
227 | self.scrollView.contentInset = newInset
228 | }, completion: { (finished) in
229 | if finished {
230 | self.state = .idle
231 | self.displayLink?.isPaused = true
232 | self.displayLink?.invalidate()
233 | self.displayLink = nil
234 | self.disappearProgress = 1
235 | }
236 | })
237 |
238 | self.displayLink = CADisplayLink(target: self, selector: #selector(SurfingRefreshControl.updateDisappearAnimation))
239 | self.displayLink?.add(to: RunLoop.main, forMode: RunLoopMode.commonModes)
240 | }
241 |
242 | func updateDisappearAnimation() {
243 | if (self.disappearProgress >= 0 && self.disappearProgress <= 1) {
244 | //60.0 means this method get called 60 times per second , 60fps
245 | self.disappearProgress = self.disappearProgress + CGFloat(1/60.0/SurfingConfigs.disappearDuration);
246 | self.updateBarItemsWithProgress(1 - self.disappearProgress)
247 |
248 | }
249 | }
250 |
251 | func barItemAnimation(_ barItem:SurfingBarItem) {
252 | if self.state == .refreshing {
253 | barItem.alpha = 1
254 | barItem.layer.removeAllAnimations()
255 | UIView.animate(withDuration: SurfingConfigs.loadingIndividualAnimationTiming, animations: {
256 | barItem.alpha = SurfingConfigs.barDarkAlpha
257 | })
258 |
259 | var isLastOne:Bool!
260 |
261 | if self.reverseLoadingAnimation == true {
262 | isLastOne = barItem.tag == 0
263 | } else {
264 | isLastOne = barItem.tag == self.barItems.count-1
265 | }
266 |
267 | //recursive
268 | if isLastOne == true {
269 | self.startLoadingAnimation()
270 | }
271 | }
272 | }
273 |
274 | }
275 |
276 |
277 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo/SurfingRefreshControl/SurfingRefreshControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SurfingRefreshControl.swift
3 | // CBStoreHouseRefreshControl
4 | //
5 | // Created by chenpeiwei on 6/8/16.
6 | // Copyright © 2016 Peiwei Chen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | fileprivate func < (lhs: T?, rhs: T?) -> Bool {
11 | switch (lhs, rhs) {
12 | case let (l?, r?):
13 | return l < r
14 | case (nil, _?):
15 | return true
16 | default:
17 | return false
18 | }
19 | }
20 |
21 | fileprivate func >= (lhs: T?, rhs: T?) -> Bool {
22 | switch (lhs, rhs) {
23 | case let (l?, r?):
24 | return l >= r
25 | default:
26 | return !(lhs < rhs)
27 | }
28 | }
29 |
30 | fileprivate func <= (lhs: T?, rhs: T?) -> Bool {
31 | switch (lhs, rhs) {
32 | case let (l?, r?):
33 | return l <= r
34 | default:
35 | return !(rhs < lhs)
36 | }
37 | }
38 |
39 | struct SurfingConfigs {
40 | static let loadingIndividualAnimationTiming = 0.8
41 | static let barDarkAlpha:CGFloat = 0.4
42 | static let loadingTimingOffset = 0.1
43 | static let disappearDuration = 1.2
44 | static let relativeHeightFactor:CGFloat = 0.4
45 | static let startPointKey = "startPoints"
46 | static let endPointKey = "endPoints"
47 | static let xKey = "x"
48 | static let yKey = "y"
49 | }
50 |
51 | enum SurfingRefreshControlState {
52 | case idle
53 | case refreshing
54 | case disappearing
55 | }
56 |
57 | class SurfingRefreshControl: UIView {
58 | weak var scrollView:UIScrollView!
59 | var state:SurfingRefreshControlState = .idle
60 | var barItems:[SurfingBarItem]!
61 | var displayLink:CADisplayLink?
62 | var target:AnyObject!
63 | var action:Selector!
64 |
65 | var dropHeight:CGFloat!
66 | var disappearProgress:CGFloat!
67 | var internalAnimationFactor:CGFloat!
68 | var horizontalRandomness:Int!
69 | var reverseLoadingAnimation:Bool!
70 |
71 | var realContentOffsetY:CGFloat {
72 | get {
73 | return self.scrollView.contentOffset.y + self.scrollView.contentInset.top
74 | }
75 | }
76 |
77 | var surfingBarItemsAnimationProgress:CGFloat {
78 | get {
79 | return min(1.0,max(0,fabs(self.realContentOffsetY/self.dropHeight)))
80 | }
81 | }
82 |
83 | class func attachToScrollView(_ scrollView:UIScrollView,target:AnyObject,refreshAction:Selector,plist:String,color:UIColor=UIColor.black,lineWidth:CGFloat=2,dropHeight:CGFloat=80,scale:CGFloat=1,horizontalRandomness:Int=150,reverseLoadingAnimation:Bool=false,internalAnimationFactor:CGFloat=1.0) -> SurfingRefreshControl{
84 |
85 | let refreshControl = SurfingRefreshControl()
86 | refreshControl.dropHeight = dropHeight
87 | refreshControl.horizontalRandomness = horizontalRandomness
88 | refreshControl.scrollView = scrollView
89 | refreshControl.target = target
90 | refreshControl.action = refreshAction
91 | refreshControl.reverseLoadingAnimation = reverseLoadingAnimation
92 | refreshControl.internalAnimationFactor = internalAnimationFactor
93 | scrollView.addSubview(refreshControl)
94 |
95 | var width:CGFloat = 0
96 | var height:CGFloat = 0
97 |
98 | let dataDictionary = NSDictionary(contentsOfFile: Bundle.main.path(forResource: plist, ofType: "plist")!)
99 | let startPoints = dataDictionary?.object(forKey: SurfingConfigs.startPointKey) as! NSArray
100 | let endPoints = dataDictionary?.object(forKey: SurfingConfigs.endPointKey) as! NSArray
101 |
102 | for (index,startPointObject) in startPoints.enumerated() {
103 | let startPoint = CGPointFromString(startPointObject as! String)
104 | let endPoint = CGPointFromString(endPoints[index] as! String)
105 |
106 | if startPoint.x > width {
107 | width = startPoint.x
108 | }
109 | if endPoint.x > width {
110 | width = endPoint.x
111 | }
112 | if startPoint.y > height {
113 | height = startPoint.y
114 | }
115 | if endPoint.y > height {
116 | height = endPoint.y
117 | }
118 | }
119 |
120 | refreshControl.frame = CGRect(x: 0, y: 0, width: width, height: height)
121 |
122 | var surfingBarItems = [SurfingBarItem]()
123 |
124 | for (index,startPointObject) in startPoints.enumerated() {
125 | let startPoint = CGPointFromString(startPointObject as! String)
126 | let endPoint = CGPointFromString(endPoints[index] as! String)
127 | let surfingBarItem = SurfingBarItem(frame: refreshControl.frame, startPoint: startPoint, endPoint: endPoint, color: color, lineWidth: lineWidth)
128 | surfingBarItem.tag = index
129 | surfingBarItem.backgroundColor = UIColor.clear
130 | surfingBarItem.alpha = 0
131 | surfingBarItem.setHorizontalRandomness(refreshControl.horizontalRandomness, dropHeight: refreshControl.dropHeight)
132 | surfingBarItems.append(surfingBarItem)
133 | refreshControl.addSubview(surfingBarItem)
134 | }
135 | refreshControl.barItems = surfingBarItems
136 | refreshControl.transform = CGAffineTransform(scaleX: scale, y: scale)
137 |
138 | return refreshControl
139 | }
140 |
141 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
142 |
143 | if scrollView != self.scrollView {
144 | return
145 | }
146 |
147 | self.center = CGPoint(x: scrollView.frame.size.width*0.5, y: self.scrollView.contentOffset.y*SurfingConfigs.relativeHeightFactor)
148 | if self.state == SurfingRefreshControlState.idle {
149 | self.updateBarItemsWithProgress(self.surfingBarItemsAnimationProgress)
150 | }
151 | }
152 |
153 |
154 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
155 | if self.state == .idle && self.realContentOffsetY < -self.dropHeight {
156 | if self.surfingBarItemsAnimationProgress == 1 {
157 | self.state = .refreshing
158 | var newInsets = self.scrollView.contentInset
159 | newInsets.top += self.dropHeight
160 | let contentOffset = self.scrollView.contentOffset
161 |
162 | UIView.animate(withDuration: 0, animations: {
163 | self.scrollView.contentInset = newInsets
164 | self.scrollView.contentOffset = contentOffset
165 | })
166 |
167 | if self.target.responds(to: self.action) {
168 | self.target.perform(self.action, with: self)
169 | }
170 | self.startLoadingAnimation()
171 | }
172 | }
173 | }
174 |
175 | func updateBarItemsWithProgress(_ progress:CGFloat) {
176 | for (index,barItem) in self.barItems.enumerated() {
177 |
178 | //internalAnimationFactor: 1 means normal animation, 0.7 cut the animation to 70% compared to normal animation, in this case , we could handle 1-0.7 = 30%.
179 |
180 |
181 | let startPadding = (1 - self.internalAnimationFactor)/CGFloat(self.barItems.count)*CGFloat(index)
182 | let endPadding = 1 - self.internalAnimationFactor - startPadding
183 |
184 | if (progress == 1 || progress >= 1 - endPadding) {
185 | barItem.transform = CGAffineTransform.identity
186 | barItem.alpha = SurfingConfigs.barDarkAlpha
187 | } else if progress == 0 {
188 | barItem.setHorizontalRandomness(self.horizontalRandomness, dropHeight: self.dropHeight)
189 | }
190 | else {
191 |
192 | var surfingProgress:CGFloat!
193 | if progress <= startPadding {
194 | //key for internalAnimationFactor
195 | surfingProgress = 0
196 | } else {
197 | surfingProgress = min(1, (progress - startPadding)/self.internalAnimationFactor)
198 | }
199 | barItem.transform = CGAffineTransform(translationX: barItem.translationX*(1-surfingProgress), y: -self.dropHeight*(1-surfingProgress))
200 | barItem.transform = barItem.transform.rotated(by: CGFloat(M_PI)*surfingProgress)
201 | barItem.transform = barItem.transform.scaledBy(x: surfingProgress, y: surfingProgress)
202 | barItem.alpha = surfingProgress * SurfingConfigs.barDarkAlpha
203 | }
204 | }
205 | }
206 |
207 | func startLoadingAnimation() {
208 |
209 | //NSRunLoopCommonModes make it possible that animating even when user's dragging scrollView
210 | if self.reverseLoadingAnimation == true {
211 | for (index,barItem) in self.barItems.reversed().enumerated() {
212 | self.perform(#selector(SurfingRefreshControl.barItemAnimation(_:)), with: barItem, afterDelay: Double(index) * SurfingConfigs.loadingTimingOffset, inModes: [RunLoopMode.commonModes])
213 | }
214 | } else {
215 | for (index,barItem) in self.barItems.enumerated() {
216 | self.perform(#selector(SurfingRefreshControl.barItemAnimation(_:)), with: barItem, afterDelay: Double(index) * SurfingConfigs.loadingTimingOffset, inModes: [RunLoopMode.commonModes])
217 | }
218 | }
219 | }
220 |
221 | func finishLoading() {
222 | self.state = .disappearing
223 | self.disappearProgress = 0
224 | var newInset = self.scrollView.contentInset
225 | newInset.top = self.scrollView.contentInset.top - self.dropHeight
226 | UIView.animate(withDuration: SurfingConfigs.disappearDuration,animations: {
227 | self.scrollView.contentInset = newInset
228 | }, completion: { (finished) in
229 | if finished {
230 | self.state = .idle
231 | self.displayLink?.isPaused = true
232 | self.displayLink?.invalidate()
233 | self.displayLink = nil
234 | self.disappearProgress = 1
235 | }
236 | })
237 |
238 | self.displayLink = CADisplayLink(target: self, selector: #selector(SurfingRefreshControl.updateDisappearAnimation))
239 | self.displayLink?.add(to: RunLoop.main, forMode: RunLoopMode.commonModes)
240 | }
241 |
242 | func updateDisappearAnimation() {
243 | if (self.disappearProgress >= 0 && self.disappearProgress <= 1) {
244 | //60.0 means this method get called 60 times per second , 60fps
245 | self.disappearProgress = self.disappearProgress + CGFloat(1/60.0/SurfingConfigs.disappearDuration);
246 | self.updateBarItemsWithProgress(1 - self.disappearProgress)
247 |
248 | }
249 | }
250 |
251 | func barItemAnimation(_ barItem:SurfingBarItem) {
252 | if self.state == .refreshing {
253 | barItem.alpha = 1
254 | barItem.layer.removeAllAnimations()
255 | UIView.animate(withDuration: SurfingConfigs.loadingIndividualAnimationTiming, animations: {
256 | barItem.alpha = SurfingConfigs.barDarkAlpha
257 | })
258 |
259 | var isLastOne:Bool!
260 |
261 | if self.reverseLoadingAnimation == true {
262 | isLastOne = barItem.tag == 0
263 | } else {
264 | isLastOne = barItem.tag == self.barItems.count-1
265 | }
266 |
267 | //recursive
268 | if isLastOne == true {
269 | self.startLoadingAnimation()
270 | }
271 | }
272 | }
273 |
274 | }
275 |
276 |
277 |
--------------------------------------------------------------------------------
/surfingRefreshControlDemo/surfingRefreshControlDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F441994C1D0BF05F00662A23 /* surfing.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F441994B1D0BF05F00662A23 /* surfing.jpg */; };
11 | F441997A1D0D7C6600662A23 /* polygon.plist in Resources */ = {isa = PBXBuildFile; fileRef = F44199741D0D7C6600662A23 /* polygon.plist */; };
12 | F441997B1D0D7C6600662A23 /* square.plist in Resources */ = {isa = PBXBuildFile; fileRef = F44199751D0D7C6600662A23 /* square.plist */; };
13 | F441997C1D0D7C6600662A23 /* storehouserefresh.plist in Resources */ = {isa = PBXBuildFile; fileRef = F44199761D0D7C6600662A23 /* storehouserefresh.plist */; };
14 | F441997D1D0D7C6600662A23 /* surfing.plist in Resources */ = {isa = PBXBuildFile; fileRef = F44199771D0D7C6600662A23 /* surfing.plist */; };
15 | F441997E1D0D7C6600662A23 /* SurfingBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F44199781D0D7C6600662A23 /* SurfingBarItem.swift */; };
16 | F441997F1D0D7C6600662A23 /* SurfingRefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F44199791D0D7C6600662A23 /* SurfingRefreshControl.swift */; };
17 | F4EEEF371D0A3D5C00FA3BB4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EEEF361D0A3D5C00FA3BB4 /* AppDelegate.swift */; };
18 | F4EEEF3E1D0A3D5D00FA3BB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4EEEF3D1D0A3D5D00FA3BB4 /* Assets.xcassets */; };
19 | F4EEEF411D0A3D5D00FA3BB4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4EEEF3F1D0A3D5D00FA3BB4 /* LaunchScreen.storyboard */; };
20 | F4EEEF571D0A3EB200FA3BB4 /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EEEF561D0A3EB200FA3BB4 /* DemoViewController.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXFileReference section */
24 | F441994B1D0BF05F00662A23 /* surfing.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = surfing.jpg; sourceTree = ""; };
25 | F44199741D0D7C6600662A23 /* polygon.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = polygon.plist; sourceTree = ""; };
26 | F44199751D0D7C6600662A23 /* square.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = square.plist; sourceTree = ""; };
27 | F44199761D0D7C6600662A23 /* storehouserefresh.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = storehouserefresh.plist; sourceTree = ""; };
28 | F44199771D0D7C6600662A23 /* surfing.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = surfing.plist; sourceTree = ""; };
29 | F44199781D0D7C6600662A23 /* SurfingBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurfingBarItem.swift; sourceTree = ""; };
30 | F44199791D0D7C6600662A23 /* SurfingRefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurfingRefreshControl.swift; sourceTree = ""; };
31 | F4EEEF331D0A3D5C00FA3BB4 /* surfingRefreshControlDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = surfingRefreshControlDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 | F4EEEF361D0A3D5C00FA3BB4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33 | F4EEEF3D1D0A3D5D00FA3BB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
34 | F4EEEF401D0A3D5D00FA3BB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
35 | F4EEEF421D0A3D5D00FA3BB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
36 | F4EEEF561D0A3EB200FA3BB4 /* DemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoViewController.swift; sourceTree = ""; };
37 | /* End PBXFileReference section */
38 |
39 | /* Begin PBXFrameworksBuildPhase section */
40 | F4EEEF301D0A3D5C00FA3BB4 /* Frameworks */ = {
41 | isa = PBXFrameworksBuildPhase;
42 | buildActionMask = 2147483647;
43 | files = (
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXFrameworksBuildPhase section */
48 |
49 | /* Begin PBXGroup section */
50 | F44199721D0D7C6600662A23 /* SurfingRefreshControl */ = {
51 | isa = PBXGroup;
52 | children = (
53 | F44199731D0D7C6600662A23 /* Plists */,
54 | F44199781D0D7C6600662A23 /* SurfingBarItem.swift */,
55 | F44199791D0D7C6600662A23 /* SurfingRefreshControl.swift */,
56 | );
57 | path = SurfingRefreshControl;
58 | sourceTree = "";
59 | };
60 | F44199731D0D7C6600662A23 /* Plists */ = {
61 | isa = PBXGroup;
62 | children = (
63 | F44199741D0D7C6600662A23 /* polygon.plist */,
64 | F44199751D0D7C6600662A23 /* square.plist */,
65 | F44199761D0D7C6600662A23 /* storehouserefresh.plist */,
66 | F44199771D0D7C6600662A23 /* surfing.plist */,
67 | );
68 | path = Plists;
69 | sourceTree = "";
70 | };
71 | F4EEEF2A1D0A3D5C00FA3BB4 = {
72 | isa = PBXGroup;
73 | children = (
74 | F4EEEF351D0A3D5C00FA3BB4 /* surfingRefreshControlDemo */,
75 | F4EEEF341D0A3D5C00FA3BB4 /* Products */,
76 | );
77 | sourceTree = "";
78 | };
79 | F4EEEF341D0A3D5C00FA3BB4 /* Products */ = {
80 | isa = PBXGroup;
81 | children = (
82 | F4EEEF331D0A3D5C00FA3BB4 /* surfingRefreshControlDemo.app */,
83 | );
84 | name = Products;
85 | sourceTree = "";
86 | };
87 | F4EEEF351D0A3D5C00FA3BB4 /* surfingRefreshControlDemo */ = {
88 | isa = PBXGroup;
89 | children = (
90 | F44199721D0D7C6600662A23 /* SurfingRefreshControl */,
91 | F4EEEF361D0A3D5C00FA3BB4 /* AppDelegate.swift */,
92 | F4EEEF561D0A3EB200FA3BB4 /* DemoViewController.swift */,
93 | F441994B1D0BF05F00662A23 /* surfing.jpg */,
94 | F4EEEF3D1D0A3D5D00FA3BB4 /* Assets.xcassets */,
95 | F4EEEF3F1D0A3D5D00FA3BB4 /* LaunchScreen.storyboard */,
96 | F4EEEF421D0A3D5D00FA3BB4 /* Info.plist */,
97 | );
98 | path = surfingRefreshControlDemo;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | F4EEEF321D0A3D5C00FA3BB4 /* surfingRefreshControlDemo */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = F4EEEF451D0A3D5D00FA3BB4 /* Build configuration list for PBXNativeTarget "surfingRefreshControlDemo" */;
107 | buildPhases = (
108 | F4EEEF2F1D0A3D5C00FA3BB4 /* Sources */,
109 | F4EEEF301D0A3D5C00FA3BB4 /* Frameworks */,
110 | F4EEEF311D0A3D5C00FA3BB4 /* Resources */,
111 | );
112 | buildRules = (
113 | );
114 | dependencies = (
115 | );
116 | name = surfingRefreshControlDemo;
117 | productName = surfingRefreshControlDemo;
118 | productReference = F4EEEF331D0A3D5C00FA3BB4 /* surfingRefreshControlDemo.app */;
119 | productType = "com.apple.product-type.application";
120 | };
121 | /* End PBXNativeTarget section */
122 |
123 | /* Begin PBXProject section */
124 | F4EEEF2B1D0A3D5C00FA3BB4 /* Project object */ = {
125 | isa = PBXProject;
126 | attributes = {
127 | LastSwiftUpdateCheck = 0730;
128 | LastUpgradeCheck = 0800;
129 | TargetAttributes = {
130 | F4EEEF321D0A3D5C00FA3BB4 = {
131 | CreatedOnToolsVersion = 7.3.1;
132 | LastSwiftMigration = 0800;
133 | };
134 | };
135 | };
136 | buildConfigurationList = F4EEEF2E1D0A3D5C00FA3BB4 /* Build configuration list for PBXProject "surfingRefreshControlDemo" */;
137 | compatibilityVersion = "Xcode 3.2";
138 | developmentRegion = English;
139 | hasScannedForEncodings = 0;
140 | knownRegions = (
141 | en,
142 | Base,
143 | );
144 | mainGroup = F4EEEF2A1D0A3D5C00FA3BB4;
145 | productRefGroup = F4EEEF341D0A3D5C00FA3BB4 /* Products */;
146 | projectDirPath = "";
147 | projectRoot = "";
148 | targets = (
149 | F4EEEF321D0A3D5C00FA3BB4 /* surfingRefreshControlDemo */,
150 | );
151 | };
152 | /* End PBXProject section */
153 |
154 | /* Begin PBXResourcesBuildPhase section */
155 | F4EEEF311D0A3D5C00FA3BB4 /* Resources */ = {
156 | isa = PBXResourcesBuildPhase;
157 | buildActionMask = 2147483647;
158 | files = (
159 | F441997D1D0D7C6600662A23 /* surfing.plist in Resources */,
160 | F441994C1D0BF05F00662A23 /* surfing.jpg in Resources */,
161 | F441997C1D0D7C6600662A23 /* storehouserefresh.plist in Resources */,
162 | F441997B1D0D7C6600662A23 /* square.plist in Resources */,
163 | F4EEEF411D0A3D5D00FA3BB4 /* LaunchScreen.storyboard in Resources */,
164 | F4EEEF3E1D0A3D5D00FA3BB4 /* Assets.xcassets in Resources */,
165 | F441997A1D0D7C6600662A23 /* polygon.plist in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXSourcesBuildPhase section */
172 | F4EEEF2F1D0A3D5C00FA3BB4 /* Sources */ = {
173 | isa = PBXSourcesBuildPhase;
174 | buildActionMask = 2147483647;
175 | files = (
176 | F441997F1D0D7C6600662A23 /* SurfingRefreshControl.swift in Sources */,
177 | F4EEEF571D0A3EB200FA3BB4 /* DemoViewController.swift in Sources */,
178 | F4EEEF371D0A3D5C00FA3BB4 /* AppDelegate.swift in Sources */,
179 | F441997E1D0D7C6600662A23 /* SurfingBarItem.swift in Sources */,
180 | );
181 | runOnlyForDeploymentPostprocessing = 0;
182 | };
183 | /* End PBXSourcesBuildPhase section */
184 |
185 | /* Begin PBXVariantGroup section */
186 | F4EEEF3F1D0A3D5D00FA3BB4 /* LaunchScreen.storyboard */ = {
187 | isa = PBXVariantGroup;
188 | children = (
189 | F4EEEF401D0A3D5D00FA3BB4 /* Base */,
190 | );
191 | name = LaunchScreen.storyboard;
192 | sourceTree = "";
193 | };
194 | /* End PBXVariantGroup section */
195 |
196 | /* Begin XCBuildConfiguration section */
197 | F4EEEF431D0A3D5D00FA3BB4 /* Debug */ = {
198 | isa = XCBuildConfiguration;
199 | buildSettings = {
200 | ALWAYS_SEARCH_USER_PATHS = NO;
201 | CLANG_ANALYZER_NONNULL = YES;
202 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
203 | CLANG_CXX_LIBRARY = "libc++";
204 | CLANG_ENABLE_MODULES = YES;
205 | CLANG_ENABLE_OBJC_ARC = YES;
206 | CLANG_WARN_BOOL_CONVERSION = YES;
207 | CLANG_WARN_CONSTANT_CONVERSION = YES;
208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
209 | CLANG_WARN_EMPTY_BODY = YES;
210 | CLANG_WARN_ENUM_CONVERSION = YES;
211 | CLANG_WARN_INFINITE_RECURSION = YES;
212 | CLANG_WARN_INT_CONVERSION = YES;
213 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
214 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
215 | CLANG_WARN_UNREACHABLE_CODE = YES;
216 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
217 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
218 | COPY_PHASE_STRIP = NO;
219 | DEBUG_INFORMATION_FORMAT = dwarf;
220 | ENABLE_STRICT_OBJC_MSGSEND = YES;
221 | ENABLE_TESTABILITY = YES;
222 | GCC_C_LANGUAGE_STANDARD = gnu99;
223 | GCC_DYNAMIC_NO_PIC = NO;
224 | GCC_NO_COMMON_BLOCKS = YES;
225 | GCC_OPTIMIZATION_LEVEL = 0;
226 | GCC_PREPROCESSOR_DEFINITIONS = (
227 | "DEBUG=1",
228 | "$(inherited)",
229 | );
230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
232 | GCC_WARN_UNDECLARED_SELECTOR = YES;
233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
234 | GCC_WARN_UNUSED_FUNCTION = YES;
235 | GCC_WARN_UNUSED_VARIABLE = YES;
236 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
237 | MTL_ENABLE_DEBUG_INFO = YES;
238 | ONLY_ACTIVE_ARCH = YES;
239 | SDKROOT = iphoneos;
240 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
241 | };
242 | name = Debug;
243 | };
244 | F4EEEF441D0A3D5D00FA3BB4 /* Release */ = {
245 | isa = XCBuildConfiguration;
246 | buildSettings = {
247 | ALWAYS_SEARCH_USER_PATHS = NO;
248 | CLANG_ANALYZER_NONNULL = YES;
249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
250 | CLANG_CXX_LIBRARY = "libc++";
251 | CLANG_ENABLE_MODULES = YES;
252 | CLANG_ENABLE_OBJC_ARC = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
256 | CLANG_WARN_EMPTY_BODY = YES;
257 | CLANG_WARN_ENUM_CONVERSION = YES;
258 | CLANG_WARN_INFINITE_RECURSION = YES;
259 | CLANG_WARN_INT_CONVERSION = YES;
260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
261 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
262 | CLANG_WARN_UNREACHABLE_CODE = YES;
263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
264 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
265 | COPY_PHASE_STRIP = NO;
266 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
267 | ENABLE_NS_ASSERTIONS = NO;
268 | ENABLE_STRICT_OBJC_MSGSEND = YES;
269 | GCC_C_LANGUAGE_STANDARD = gnu99;
270 | GCC_NO_COMMON_BLOCKS = YES;
271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
273 | GCC_WARN_UNDECLARED_SELECTOR = YES;
274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
275 | GCC_WARN_UNUSED_FUNCTION = YES;
276 | GCC_WARN_UNUSED_VARIABLE = YES;
277 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
278 | MTL_ENABLE_DEBUG_INFO = NO;
279 | SDKROOT = iphoneos;
280 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
281 | VALIDATE_PRODUCT = YES;
282 | };
283 | name = Release;
284 | };
285 | F4EEEF461D0A3D5D00FA3BB4 /* Debug */ = {
286 | isa = XCBuildConfiguration;
287 | buildSettings = {
288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
289 | CLANG_ENABLE_MODULES = YES;
290 | INFOPLIST_FILE = surfingRefreshControlDemo/Info.plist;
291 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
293 | PRODUCT_BUNDLE_IDENTIFIER = peiwei.surfingRefreshControlDemo;
294 | PRODUCT_NAME = "$(TARGET_NAME)";
295 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
296 | SWIFT_VERSION = 3.0;
297 | TARGETED_DEVICE_FAMILY = "1,2";
298 | };
299 | name = Debug;
300 | };
301 | F4EEEF471D0A3D5D00FA3BB4 /* Release */ = {
302 | isa = XCBuildConfiguration;
303 | buildSettings = {
304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
305 | CLANG_ENABLE_MODULES = YES;
306 | INFOPLIST_FILE = surfingRefreshControlDemo/Info.plist;
307 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
308 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
309 | PRODUCT_BUNDLE_IDENTIFIER = peiwei.surfingRefreshControlDemo;
310 | PRODUCT_NAME = "$(TARGET_NAME)";
311 | SWIFT_VERSION = 3.0;
312 | TARGETED_DEVICE_FAMILY = "1,2";
313 | };
314 | name = Release;
315 | };
316 | /* End XCBuildConfiguration section */
317 |
318 | /* Begin XCConfigurationList section */
319 | F4EEEF2E1D0A3D5C00FA3BB4 /* Build configuration list for PBXProject "surfingRefreshControlDemo" */ = {
320 | isa = XCConfigurationList;
321 | buildConfigurations = (
322 | F4EEEF431D0A3D5D00FA3BB4 /* Debug */,
323 | F4EEEF441D0A3D5D00FA3BB4 /* Release */,
324 | );
325 | defaultConfigurationIsVisible = 0;
326 | defaultConfigurationName = Release;
327 | };
328 | F4EEEF451D0A3D5D00FA3BB4 /* Build configuration list for PBXNativeTarget "surfingRefreshControlDemo" */ = {
329 | isa = XCConfigurationList;
330 | buildConfigurations = (
331 | F4EEEF461D0A3D5D00FA3BB4 /* Debug */,
332 | F4EEEF471D0A3D5D00FA3BB4 /* Release */,
333 | );
334 | defaultConfigurationIsVisible = 0;
335 | defaultConfigurationName = Release;
336 | };
337 | /* End XCConfigurationList section */
338 | };
339 | rootObject = F4EEEF2B1D0A3D5C00FA3BB4 /* Project object */;
340 | }
341 |
--------------------------------------------------------------------------------