├── .DS_Store ├── .gitignore ├── LICENSE ├── README-chiness.md ├── README.md ├── SJLineRefresh.podspec ├── SJLineRefresh.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── SJLineRefresh ├── .DS_Store ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── backImg.imageset │ │ ├── Contents.json │ │ └── hh_chat_pay_icon.png │ ├── bg_pattern.imageset │ │ ├── Contents.json │ │ └── bg_pattern@2x.png │ ├── hh_refresh_center.imageset │ │ ├── Contents.json │ │ └── hh_refresh_center.png │ └── timg.imageset │ │ ├── Contents.json │ │ └── timg.jpg ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Demo │ ├── .DS_Store │ ├── Cell │ │ ├── SJSectionCell.swift │ │ ├── SJStyleCell.swift │ │ └── SJStyleCell.xib │ ├── Demo+extension.swift │ ├── SJBaseDemoController.swift │ ├── SJDemoListController.swift │ ├── SJDemoViewController.swift │ ├── SJNormalUseController.swift │ ├── SJStoreHouseController.swift │ └── View │ │ ├── SJConfigCell.swift │ │ ├── SJConfigModel.swift │ │ ├── SJConfigSectionCell.swift │ │ ├── SJConfigView.swift │ │ ├── SJConfigView.xib │ │ ├── SJHeaderView.swift │ │ ├── SJStepSlider.swift │ │ ├── SJTableViewCell.xib │ │ └── SJTestCell.swift ├── Info.plist ├── Resources │ ├── .DS_Store │ ├── AKTA.plist │ ├── LOL.plist │ ├── Other.plist │ ├── SJLineRefresh.bundle │ │ └── hh_refresh_center@3x.png │ ├── polygon.plist │ └── storehouse.plist └── Sources │ ├── SJAnimateConfig.swift │ ├── SJConstants.swift │ ├── SJLinePathView.swift │ ├── SJRefreshConfig.swift │ ├── SJRefreshManager.swift │ ├── SJRefreshView.swift │ └── SJScrollViewExtention.swift ├── _config.yml └── imgs ├── .DS_Store ├── AKTA.gif ├── LOL.gif ├── SJLineRefresh-icon.png ├── debug.gif ├── paintCode.png ├── polygon.gif ├── stay.gif └── step.gif /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 _Ashen 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-chiness.md: -------------------------------------------------------------------------------- 1 | # SJLineRefresh 2 | 3 | ![](https://img.shields.io/cocoapods/p/SJLineRefresh.svg?style=flat) ![](https://img.shields.io/badge/language-swift5-orange.svg) 4 | 5 | SJLineRefresh 是用 swift5 写的高度自定义的线性下拉刷新控件。 6 | 7 | - **介绍** 8 | *** 9 | 高度自定义的线性下拉刷新控件 10 | 11 | - **使用** 12 | *** 13 | ` 1.基础用法` 14 | ```swift 15 | let aPath = Bundle.main.path(forResource: "HHMedic", ofType: "plist")! 16 | let aConfig = SJRefreshConfig(plist: aPath) 17 | tableView.sj_header = SJRefreshView(config: aConfig) { [weak self] in 18 | // do your refresh 19 | } 20 | ``` 21 | ` 2.全局使用` 22 | ```swift 23 | let aPath = getCurrentBundle().path(forResource: "HHMedic", ofType: "plist") ?? "" 24 | SJRefreshManager.default.defaultConfig = SJRefreshConfig(plist: aPath) // 设置全局config 25 | tableView.sj_header = SJRefreshView.default { [weak self] in 26 | // 刷新回调 27 | // 结束刷新 28 | self?.tableView.endRefresh() 29 | } 30 | // plist只加载一次 31 | ``` 32 | ` 3.手动刷新/结束` 33 | ```swift 34 | tableView.beginRefresh() // 手动开始刷新 35 | tableView.endRefresh() // 结束刷新 36 | ``` 37 | 38 | ` 4.动画参数` 39 | ```swift 40 | startRatio // 开始动画的起始点与整个高度的比例 41 | endRatio // 结束动画的起始点与整个高度的比例 42 | stepLength // 每个line"闪烁"所用时间 43 | stepDuration // "闪烁"的动画时长 44 | style // 动画类型 45 | normal // 默认 46 | stay // "闪烁"完成后保持闪烁状态 47 | step // 下拉时,一个line绘制完成后才继续下一个绘制 48 | reverse //从最后一个line往前"闪烁 49 | ``` 50 | ![debug](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/debug.gif) 51 | 52 | - **创建形状** 53 | *** 54 | 55 | ```description 56 | - 使用PaintCode画线, 在plist中写入对应的所有线条的起始点和终点(数量要保持一致) 57 | - 下拉刷新视图的宽高等于起点和终点的最大距离, 居中对齐 58 | ``` 59 | 60 | - **安装** 61 | *** 62 | **CocoaPods** 63 | 64 | ```ruby 65 | pod 'SJLineRefresh', '~> 1.1.1' 66 | ``` 67 | **Carthage** 68 | ``` 69 | github "515783034/SJLIineRefresh" ~> 1.1.1 70 | ``` 71 | 72 | - **参考** 73 | *** 74 | [MJRefresh](https://github.com/CoderMJLee/MJRefresh) 75 | 76 | [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl) 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/SJLineRefresh-icon.png) 2 | 3 | ![](https://img.shields.io/cocoapods/p/SJLineRefresh.svg?style=flat) ![](https://img.shields.io/badge/language-swift5-orange.svg) 4 | 5 | SJLineRefresh is an easy customizable shape pull-to-refresh library written in Swift5. 6 | 7 | - **what's this?** 8 | *** 9 | A easy customizable shape pull-to-refresh control [中文介绍](./README-chiness.md) 10 | 11 | ![pology](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/polygon.gif) ![LOL](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/LOL.gif) 12 | 13 | ![AKTA](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/AKTA.gif) ![stay -style](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/stay.gif) 14 | 15 | ![step -style](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/step.gif) 16 | 17 | 18 | - **how to use** 19 | *** 20 | ``` 21 | let aPath = Bundle.main.path(forResource: "HHMedic", ofType: "plist")! 22 | let aConfig = SJRefreshConfig(plist: aPath) 23 | tableView.sj_header = SJRefreshView(config: aConfig) { [weak self] in 24 | // do your refresh 25 | } 26 | 27 | ``` 28 | 29 | - **how to create shape** 30 | *** 31 | only support line shape now. 32 | 33 | [PaintCode](https://www.paintcodeapp.com) is a app that can turn drawings into Objective-C or swift code. 34 | ![paintCode](https://github.com/515783034/SJLIineRefresh/blob/master/imgs/paintCode.png) 35 | 36 | ``` 37 | - drawing lines whatever you want; 38 | - paste the points that PaintCode genarated into your own plist(startPoints, endPoint); 39 | - the refreshView's maxWidth is equal to the max x of points in step 2. so does the maxHeight. 40 | ``` 41 | 42 | - **Installation** 43 | *** 44 | **CocoaPods** 45 | 46 | [CocoaPods](http://cocoapods.org/) is a dependency manager for Cocoa projects. 47 | ``` 48 | $ gem install cocoapods 49 | ``` 50 | 51 | To integrate Alamofire into your Xcode project using CocoaPods, specify it in your Podfile 52 | : 53 | ``` 54 | pod 'SJLineRefresh', '~> 1.1.1' 55 | ``` 56 | Then, run the following command: 57 | ``` 58 | $ pod install 59 | ``` 60 | 61 | **Carthage** 62 | 63 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 64 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 65 | ``` 66 | $ brew update 67 | $ brew install carthage 68 | ``` 69 | To integrate Alamofire into your Xcode project using Carthage, specify it in your Cartfile : 70 | ``` 71 | github "515783034/SJLIineRefresh" ~> 1.1.1 72 | ``` 73 | Run carthage update to build the framework and drag the built SJLineRefresh.framework into your Xcode project. 74 | 75 | - **Reference** 76 | *** 77 | [MJRefresh](https://github.com/CoderMJLee/MJRefresh) 78 | 79 | [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl) 80 | -------------------------------------------------------------------------------- /SJLineRefresh.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | 4 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 5 | 6 | s.name = "SJLineRefresh" 7 | s.version = "1.1.6" 8 | s.summary = "A easy customizable shape pull-to-refresh view in UIScrolView." 9 | s.swift_version = '5' 10 | 11 | 12 | s.description = <<-DESC 13 | A pull-to-refresh control that you could use your own share easily. 14 | DESC 15 | 16 | s.homepage = "https://github.com/515783034/SJLineRefresh" 17 | s.screenshots = "http://upload-images.jianshu.io/upload_images/988961-5f6cc8c69fead46f.gif?imageMogr2/auto-orient/strip" 18 | 19 | s.license = "MIT" 20 | 21 | s.author = { "shmily" => "shmilyshijian@foxmail.com" } 22 | s.social_media_url = "http://www.jianshu.com/u/3095a094665c" 23 | 24 | s.platform = :ios, "9.0" 25 | 26 | s.source = { :git => "https://github.com/515783034/SJLineRefresh.git", :tag => "#{s.version}" } 27 | 28 | s.source_files = "SJLineRefresh/Sources/*.swift" 29 | 30 | s.resources = "SJLineRefresh/Resources/*.*" 31 | 32 | s.requires_arc = true 33 | 34 | end 35 | -------------------------------------------------------------------------------- /SJLineRefresh.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 952765701ED6F166003B14AB /* SJDemoListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9527656F1ED6F166003B14AB /* SJDemoListController.swift */; }; 11 | 954089F9238BAACB00A93D4B /* Other.plist in Resources */ = {isa = PBXBuildFile; fileRef = 954089F8238BAACB00A93D4B /* Other.plist */; }; 12 | 956407D11ECEF3AE00ED9239 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407D01ECEF3AE00ED9239 /* AppDelegate.swift */; }; 13 | 956407D61ECEF3AE00ED9239 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 956407D41ECEF3AE00ED9239 /* Main.storyboard */; }; 14 | 956407D81ECEF3AE00ED9239 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 956407D71ECEF3AE00ED9239 /* Assets.xcassets */; }; 15 | 956407DB1ECEF3AE00ED9239 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 956407D91ECEF3AE00ED9239 /* LaunchScreen.storyboard */; }; 16 | 956407E51ECEF47C00ED9239 /* SJRefreshView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407E41ECEF47C00ED9239 /* SJRefreshView.swift */; }; 17 | 956407E71ECEF4A300ED9239 /* SJLinePathView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407E61ECEF4A300ED9239 /* SJLinePathView.swift */; }; 18 | 956407ED1ECEFB5600ED9239 /* SJRefreshConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407EC1ECEFB5600ED9239 /* SJRefreshConfig.swift */; }; 19 | 956407EF1ED01DBE00ED9239 /* SJConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407EE1ED01DBE00ED9239 /* SJConstants.swift */; }; 20 | 956407F31ED054D700ED9239 /* SJScrollViewExtention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407F21ED054D700ED9239 /* SJScrollViewExtention.swift */; }; 21 | 956407F71ED05EA300ED9239 /* SJAnimateConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956407F61ED05EA300ED9239 /* SJAnimateConfig.swift */; }; 22 | 958AAA1C1ED6EE5400E4F512 /* SJDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958AAA1A1ED6EE5400E4F512 /* SJDemoViewController.swift */; }; 23 | 958D8C521EE953480031CDA5 /* SJHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C4B1EE953480031CDA5 /* SJHeaderView.swift */; }; 24 | 958D8C531EE953480031CDA5 /* SJConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C4C1EE953480031CDA5 /* SJConfigModel.swift */; }; 25 | 958D8C541EE953480031CDA5 /* SJConfigSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C4D1EE953480031CDA5 /* SJConfigSectionCell.swift */; }; 26 | 958D8C551EE953480031CDA5 /* SJStepSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C4E1EE953480031CDA5 /* SJStepSlider.swift */; }; 27 | 958D8C561EE953480031CDA5 /* SJConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C4F1EE953480031CDA5 /* SJConfigView.swift */; }; 28 | 958D8C571EE953480031CDA5 /* SJConfigView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 958D8C501EE953480031CDA5 /* SJConfigView.xib */; }; 29 | 958D8C581EE953480031CDA5 /* SJConfigCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C511EE953480031CDA5 /* SJConfigCell.swift */; }; 30 | 959623751F0F7E95004CD54E /* AKTA.plist in Resources */ = {isa = PBXBuildFile; fileRef = 959623711F0F7E95004CD54E /* AKTA.plist */; }; 31 | 959623761F0F7E95004CD54E /* LOL.plist in Resources */ = {isa = PBXBuildFile; fileRef = 959623721F0F7E95004CD54E /* LOL.plist */; }; 32 | 959623771F0F7E95004CD54E /* polygon.plist in Resources */ = {isa = PBXBuildFile; fileRef = 959623731F0F7E95004CD54E /* polygon.plist */; }; 33 | 959623781F0F7E95004CD54E /* storehouse.plist in Resources */ = {isa = PBXBuildFile; fileRef = 959623741F0F7E95004CD54E /* storehouse.plist */; }; 34 | 9599390B1EF93420004784A2 /* SJTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 959939091EF93420004784A2 /* SJTableViewCell.xib */; }; 35 | 9599390D1EF934D6004784A2 /* SJTestCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9599390C1EF934D6004784A2 /* SJTestCell.swift */; }; 36 | 959939101EF937D0004784A2 /* SJSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9599390F1EF937D0004784A2 /* SJSectionCell.swift */; }; 37 | 95C6B79A1EFA624F00E5B8C1 /* SJLineRefresh.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 95C6B7981EFA624F00E5B8C1 /* SJLineRefresh.bundle */; }; 38 | 95D822211EF7C5B7007C1CBB /* Demo+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D822201EF7C5B7007C1CBB /* Demo+extension.swift */; }; 39 | 95D822231EF7C943007C1CBB /* SJNormalUseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D822221EF7C943007C1CBB /* SJNormalUseController.swift */; }; 40 | 95D822251EF7CA86007C1CBB /* SJBaseDemoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D822241EF7CA86007C1CBB /* SJBaseDemoController.swift */; }; 41 | 95D8222B1EF8B9FB007C1CBB /* SJStoreHouseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8222A1EF8B9FB007C1CBB /* SJStoreHouseController.swift */; }; 42 | 95DA684B1ED29998003FAB74 /* SJRefreshManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95DA684A1ED29998003FAB74 /* SJRefreshManager.swift */; }; 43 | DAB7B32E1F069E5B00D0CBEE /* SJStyleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7B32C1F069E5B00D0CBEE /* SJStyleCell.swift */; }; 44 | DAB7B32F1F069E5B00D0CBEE /* SJStyleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAB7B32D1F069E5B00D0CBEE /* SJStyleCell.xib */; }; 45 | /* End PBXBuildFile section */ 46 | 47 | /* Begin PBXCopyFilesBuildPhase section */ 48 | 958B02631EFA6F100081285F /* Embed Frameworks */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = ""; 52 | dstSubfolderSpec = 10; 53 | files = ( 54 | ); 55 | name = "Embed Frameworks"; 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXCopyFilesBuildPhase section */ 59 | 60 | /* Begin PBXFileReference section */ 61 | 9527656F1ED6F166003B14AB /* SJDemoListController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJDemoListController.swift; sourceTree = ""; }; 62 | 954089F8238BAACB00A93D4B /* Other.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Other.plist; sourceTree = ""; }; 63 | 956407CD1ECEF3AE00ED9239 /* SJLineRefresh.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SJLineRefresh.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 956407D01ECEF3AE00ED9239 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | 956407D51ECEF3AE00ED9239 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 66 | 956407D71ECEF3AE00ED9239 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 67 | 956407DA1ECEF3AE00ED9239 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 68 | 956407DC1ECEF3AE00ED9239 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 956407E41ECEF47C00ED9239 /* SJRefreshView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJRefreshView.swift; sourceTree = ""; }; 70 | 956407E61ECEF4A300ED9239 /* SJLinePathView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJLinePathView.swift; sourceTree = ""; }; 71 | 956407EC1ECEFB5600ED9239 /* SJRefreshConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJRefreshConfig.swift; sourceTree = ""; }; 72 | 956407EE1ED01DBE00ED9239 /* SJConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJConstants.swift; sourceTree = ""; }; 73 | 956407F21ED054D700ED9239 /* SJScrollViewExtention.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJScrollViewExtention.swift; sourceTree = ""; }; 74 | 956407F61ED05EA300ED9239 /* SJAnimateConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJAnimateConfig.swift; sourceTree = ""; }; 75 | 958AAA1A1ED6EE5400E4F512 /* SJDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJDemoViewController.swift; sourceTree = ""; }; 76 | 958D8C4B1EE953480031CDA5 /* SJHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJHeaderView.swift; sourceTree = ""; }; 77 | 958D8C4C1EE953480031CDA5 /* SJConfigModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJConfigModel.swift; sourceTree = ""; }; 78 | 958D8C4D1EE953480031CDA5 /* SJConfigSectionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJConfigSectionCell.swift; sourceTree = ""; }; 79 | 958D8C4E1EE953480031CDA5 /* SJStepSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJStepSlider.swift; sourceTree = ""; }; 80 | 958D8C4F1EE953480031CDA5 /* SJConfigView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJConfigView.swift; sourceTree = ""; }; 81 | 958D8C501EE953480031CDA5 /* SJConfigView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SJConfigView.xib; sourceTree = ""; }; 82 | 958D8C511EE953480031CDA5 /* SJConfigCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SJConfigCell.swift; path = ../View/SJConfigCell.swift; sourceTree = ""; }; 83 | 959623711F0F7E95004CD54E /* AKTA.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AKTA.plist; sourceTree = ""; }; 84 | 959623721F0F7E95004CD54E /* LOL.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = LOL.plist; sourceTree = ""; }; 85 | 959623731F0F7E95004CD54E /* polygon.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = polygon.plist; sourceTree = ""; }; 86 | 959623741F0F7E95004CD54E /* storehouse.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = storehouse.plist; sourceTree = ""; }; 87 | 959939091EF93420004784A2 /* SJTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SJTableViewCell.xib; path = ../View/SJTableViewCell.xib; sourceTree = ""; }; 88 | 9599390C1EF934D6004784A2 /* SJTestCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SJTestCell.swift; path = ../View/SJTestCell.swift; sourceTree = ""; }; 89 | 9599390F1EF937D0004784A2 /* SJSectionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJSectionCell.swift; sourceTree = ""; }; 90 | 95C6B7981EFA624F00E5B8C1 /* SJLineRefresh.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SJLineRefresh.bundle; sourceTree = ""; }; 91 | 95D822201EF7C5B7007C1CBB /* Demo+extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Demo+extension.swift"; sourceTree = ""; }; 92 | 95D822221EF7C943007C1CBB /* SJNormalUseController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJNormalUseController.swift; sourceTree = ""; }; 93 | 95D822241EF7CA86007C1CBB /* SJBaseDemoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJBaseDemoController.swift; sourceTree = ""; }; 94 | 95D8222A1EF8B9FB007C1CBB /* SJStoreHouseController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJStoreHouseController.swift; sourceTree = ""; }; 95 | 95DA684A1ED29998003FAB74 /* SJRefreshManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJRefreshManager.swift; sourceTree = ""; }; 96 | DAB7B32C1F069E5B00D0CBEE /* SJStyleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SJStyleCell.swift; sourceTree = ""; }; 97 | DAB7B32D1F069E5B00D0CBEE /* SJStyleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SJStyleCell.xib; sourceTree = ""; }; 98 | /* End PBXFileReference section */ 99 | 100 | /* Begin PBXFrameworksBuildPhase section */ 101 | 956407CA1ECEF3AE00ED9239 /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | /* End PBXFrameworksBuildPhase section */ 109 | 110 | /* Begin PBXGroup section */ 111 | 956407C41ECEF3AE00ED9239 = { 112 | isa = PBXGroup; 113 | children = ( 114 | 956407CF1ECEF3AE00ED9239 /* SJLineRefresh */, 115 | 956407CE1ECEF3AE00ED9239 /* Products */, 116 | ); 117 | sourceTree = ""; 118 | }; 119 | 956407CE1ECEF3AE00ED9239 /* Products */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 956407CD1ECEF3AE00ED9239 /* SJLineRefresh.app */, 123 | ); 124 | name = Products; 125 | sourceTree = ""; 126 | }; 127 | 956407CF1ECEF3AE00ED9239 /* SJLineRefresh */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 958AAA161ED6EDF500E4F512 /* Demo */, 131 | 956407E31ECEF46D00ED9239 /* Sources */, 132 | 956407E21ECEF46800ED9239 /* Resources */, 133 | 956407D01ECEF3AE00ED9239 /* AppDelegate.swift */, 134 | 956407D41ECEF3AE00ED9239 /* Main.storyboard */, 135 | 956407D71ECEF3AE00ED9239 /* Assets.xcassets */, 136 | 956407D91ECEF3AE00ED9239 /* LaunchScreen.storyboard */, 137 | 956407DC1ECEF3AE00ED9239 /* Info.plist */, 138 | ); 139 | path = SJLineRefresh; 140 | sourceTree = ""; 141 | }; 142 | 956407E21ECEF46800ED9239 /* Resources */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 954089F8238BAACB00A93D4B /* Other.plist */, 146 | 959623711F0F7E95004CD54E /* AKTA.plist */, 147 | 959623721F0F7E95004CD54E /* LOL.plist */, 148 | 959623731F0F7E95004CD54E /* polygon.plist */, 149 | 959623741F0F7E95004CD54E /* storehouse.plist */, 150 | 95C6B7981EFA624F00E5B8C1 /* SJLineRefresh.bundle */, 151 | ); 152 | path = Resources; 153 | sourceTree = ""; 154 | }; 155 | 956407E31ECEF46D00ED9239 /* Sources */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 956407E41ECEF47C00ED9239 /* SJRefreshView.swift */, 159 | 956407E61ECEF4A300ED9239 /* SJLinePathView.swift */, 160 | 956407EC1ECEFB5600ED9239 /* SJRefreshConfig.swift */, 161 | 956407EE1ED01DBE00ED9239 /* SJConstants.swift */, 162 | 956407F21ED054D700ED9239 /* SJScrollViewExtention.swift */, 163 | 956407F61ED05EA300ED9239 /* SJAnimateConfig.swift */, 164 | 95DA684A1ED29998003FAB74 /* SJRefreshManager.swift */, 165 | ); 166 | path = Sources; 167 | sourceTree = ""; 168 | }; 169 | 958AAA161ED6EDF500E4F512 /* Demo */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 958D8C4A1EE953480031CDA5 /* View */, 173 | 9599390E1EF937BF004784A2 /* Cell */, 174 | 9527656F1ED6F166003B14AB /* SJDemoListController.swift */, 175 | 95D822221EF7C943007C1CBB /* SJNormalUseController.swift */, 176 | 95D8222A1EF8B9FB007C1CBB /* SJStoreHouseController.swift */, 177 | 958AAA1A1ED6EE5400E4F512 /* SJDemoViewController.swift */, 178 | 95D822241EF7CA86007C1CBB /* SJBaseDemoController.swift */, 179 | 95D822201EF7C5B7007C1CBB /* Demo+extension.swift */, 180 | ); 181 | path = Demo; 182 | sourceTree = ""; 183 | }; 184 | 958D8C4A1EE953480031CDA5 /* View */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 958D8C4B1EE953480031CDA5 /* SJHeaderView.swift */, 188 | 958D8C4C1EE953480031CDA5 /* SJConfigModel.swift */, 189 | 958D8C4D1EE953480031CDA5 /* SJConfigSectionCell.swift */, 190 | 958D8C4E1EE953480031CDA5 /* SJStepSlider.swift */, 191 | 958D8C4F1EE953480031CDA5 /* SJConfigView.swift */, 192 | 958D8C501EE953480031CDA5 /* SJConfigView.xib */, 193 | ); 194 | path = View; 195 | sourceTree = ""; 196 | }; 197 | 9599390E1EF937BF004784A2 /* Cell */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | 9599390C1EF934D6004784A2 /* SJTestCell.swift */, 201 | 958D8C511EE953480031CDA5 /* SJConfigCell.swift */, 202 | 959939091EF93420004784A2 /* SJTableViewCell.xib */, 203 | 9599390F1EF937D0004784A2 /* SJSectionCell.swift */, 204 | DAB7B32C1F069E5B00D0CBEE /* SJStyleCell.swift */, 205 | DAB7B32D1F069E5B00D0CBEE /* SJStyleCell.xib */, 206 | ); 207 | path = Cell; 208 | sourceTree = ""; 209 | }; 210 | /* End PBXGroup section */ 211 | 212 | /* Begin PBXNativeTarget section */ 213 | 956407CC1ECEF3AE00ED9239 /* SJLineRefresh */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = 956407DF1ECEF3AE00ED9239 /* Build configuration list for PBXNativeTarget "SJLineRefresh" */; 216 | buildPhases = ( 217 | 956407C91ECEF3AE00ED9239 /* Sources */, 218 | 956407CA1ECEF3AE00ED9239 /* Frameworks */, 219 | 956407CB1ECEF3AE00ED9239 /* Resources */, 220 | 958B02631EFA6F100081285F /* Embed Frameworks */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | ); 226 | name = SJLineRefresh; 227 | productName = SJLineRefresh; 228 | productReference = 956407CD1ECEF3AE00ED9239 /* SJLineRefresh.app */; 229 | productType = "com.apple.product-type.application"; 230 | }; 231 | /* End PBXNativeTarget section */ 232 | 233 | /* Begin PBXProject section */ 234 | 956407C51ECEF3AE00ED9239 /* Project object */ = { 235 | isa = PBXProject; 236 | attributes = { 237 | CLASSPREFIX = SJ; 238 | LastSwiftUpdateCheck = 0830; 239 | LastUpgradeCheck = 1000; 240 | ORGANIZATIONNAME = "Shi Jian"; 241 | TargetAttributes = { 242 | 956407CC1ECEF3AE00ED9239 = { 243 | CreatedOnToolsVersion = 8.3.2; 244 | DevelopmentTeam = 773A426V2P; 245 | LastSwiftMigration = 1020; 246 | ProvisioningStyle = Automatic; 247 | }; 248 | }; 249 | }; 250 | buildConfigurationList = 956407C81ECEF3AE00ED9239 /* Build configuration list for PBXProject "SJLineRefresh" */; 251 | compatibilityVersion = "Xcode 3.2"; 252 | developmentRegion = en; 253 | hasScannedForEncodings = 0; 254 | knownRegions = ( 255 | en, 256 | Base, 257 | ); 258 | mainGroup = 956407C41ECEF3AE00ED9239; 259 | productRefGroup = 956407CE1ECEF3AE00ED9239 /* Products */; 260 | projectDirPath = ""; 261 | projectRoot = ""; 262 | targets = ( 263 | 956407CC1ECEF3AE00ED9239 /* SJLineRefresh */, 264 | ); 265 | }; 266 | /* End PBXProject section */ 267 | 268 | /* Begin PBXResourcesBuildPhase section */ 269 | 956407CB1ECEF3AE00ED9239 /* Resources */ = { 270 | isa = PBXResourcesBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | 959623781F0F7E95004CD54E /* storehouse.plist in Resources */, 274 | 959623761F0F7E95004CD54E /* LOL.plist in Resources */, 275 | 95C6B79A1EFA624F00E5B8C1 /* SJLineRefresh.bundle in Resources */, 276 | 954089F9238BAACB00A93D4B /* Other.plist in Resources */, 277 | 956407DB1ECEF3AE00ED9239 /* LaunchScreen.storyboard in Resources */, 278 | 9599390B1EF93420004784A2 /* SJTableViewCell.xib in Resources */, 279 | DAB7B32F1F069E5B00D0CBEE /* SJStyleCell.xib in Resources */, 280 | 956407D81ECEF3AE00ED9239 /* Assets.xcassets in Resources */, 281 | 959623751F0F7E95004CD54E /* AKTA.plist in Resources */, 282 | 959623771F0F7E95004CD54E /* polygon.plist in Resources */, 283 | 958D8C571EE953480031CDA5 /* SJConfigView.xib in Resources */, 284 | 956407D61ECEF3AE00ED9239 /* Main.storyboard in Resources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXResourcesBuildPhase section */ 289 | 290 | /* Begin PBXSourcesBuildPhase section */ 291 | 956407C91ECEF3AE00ED9239 /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | 958D8C581EE953480031CDA5 /* SJConfigCell.swift in Sources */, 296 | 958D8C541EE953480031CDA5 /* SJConfigSectionCell.swift in Sources */, 297 | 958AAA1C1ED6EE5400E4F512 /* SJDemoViewController.swift in Sources */, 298 | 952765701ED6F166003B14AB /* SJDemoListController.swift in Sources */, 299 | 958D8C551EE953480031CDA5 /* SJStepSlider.swift in Sources */, 300 | 958D8C521EE953480031CDA5 /* SJHeaderView.swift in Sources */, 301 | 956407E71ECEF4A300ED9239 /* SJLinePathView.swift in Sources */, 302 | 95D822231EF7C943007C1CBB /* SJNormalUseController.swift in Sources */, 303 | DAB7B32E1F069E5B00D0CBEE /* SJStyleCell.swift in Sources */, 304 | 956407F31ED054D700ED9239 /* SJScrollViewExtention.swift in Sources */, 305 | 95D822251EF7CA86007C1CBB /* SJBaseDemoController.swift in Sources */, 306 | 95D822211EF7C5B7007C1CBB /* Demo+extension.swift in Sources */, 307 | 95D8222B1EF8B9FB007C1CBB /* SJStoreHouseController.swift in Sources */, 308 | 956407D11ECEF3AE00ED9239 /* AppDelegate.swift in Sources */, 309 | 956407E51ECEF47C00ED9239 /* SJRefreshView.swift in Sources */, 310 | 959939101EF937D0004784A2 /* SJSectionCell.swift in Sources */, 311 | 956407ED1ECEFB5600ED9239 /* SJRefreshConfig.swift in Sources */, 312 | 9599390D1EF934D6004784A2 /* SJTestCell.swift in Sources */, 313 | 956407F71ED05EA300ED9239 /* SJAnimateConfig.swift in Sources */, 314 | 95DA684B1ED29998003FAB74 /* SJRefreshManager.swift in Sources */, 315 | 958D8C561EE953480031CDA5 /* SJConfigView.swift in Sources */, 316 | 958D8C531EE953480031CDA5 /* SJConfigModel.swift in Sources */, 317 | 956407EF1ED01DBE00ED9239 /* SJConstants.swift in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | /* End PBXSourcesBuildPhase section */ 322 | 323 | /* Begin PBXVariantGroup section */ 324 | 956407D41ECEF3AE00ED9239 /* Main.storyboard */ = { 325 | isa = PBXVariantGroup; 326 | children = ( 327 | 956407D51ECEF3AE00ED9239 /* Base */, 328 | ); 329 | name = Main.storyboard; 330 | sourceTree = ""; 331 | }; 332 | 956407D91ECEF3AE00ED9239 /* LaunchScreen.storyboard */ = { 333 | isa = PBXVariantGroup; 334 | children = ( 335 | 956407DA1ECEF3AE00ED9239 /* Base */, 336 | ); 337 | name = LaunchScreen.storyboard; 338 | sourceTree = ""; 339 | }; 340 | /* End PBXVariantGroup section */ 341 | 342 | /* Begin XCBuildConfiguration section */ 343 | 956407DD1ECEF3AE00ED9239 /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ALWAYS_SEARCH_USER_PATHS = NO; 347 | CLANG_ANALYZER_NONNULL = YES; 348 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 349 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 350 | CLANG_CXX_LIBRARY = "libc++"; 351 | CLANG_ENABLE_MODULES = YES; 352 | CLANG_ENABLE_OBJC_ARC = YES; 353 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 354 | CLANG_WARN_BOOL_CONVERSION = YES; 355 | CLANG_WARN_COMMA = YES; 356 | CLANG_WARN_CONSTANT_CONVERSION = YES; 357 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 358 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 359 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 360 | CLANG_WARN_EMPTY_BODY = YES; 361 | CLANG_WARN_ENUM_CONVERSION = YES; 362 | CLANG_WARN_INFINITE_RECURSION = YES; 363 | CLANG_WARN_INT_CONVERSION = YES; 364 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 365 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 366 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 367 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 368 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 369 | CLANG_WARN_STRICT_PROTOTYPES = YES; 370 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 371 | CLANG_WARN_UNREACHABLE_CODE = YES; 372 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 373 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 374 | COPY_PHASE_STRIP = NO; 375 | DEBUG_INFORMATION_FORMAT = dwarf; 376 | ENABLE_STRICT_OBJC_MSGSEND = YES; 377 | ENABLE_TESTABILITY = YES; 378 | GCC_C_LANGUAGE_STANDARD = gnu99; 379 | GCC_DYNAMIC_NO_PIC = NO; 380 | GCC_NO_COMMON_BLOCKS = YES; 381 | GCC_OPTIMIZATION_LEVEL = 0; 382 | GCC_PREPROCESSOR_DEFINITIONS = ( 383 | "DEBUG=1", 384 | "$(inherited)", 385 | ); 386 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 387 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 388 | GCC_WARN_UNDECLARED_SELECTOR = YES; 389 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 390 | GCC_WARN_UNUSED_FUNCTION = YES; 391 | GCC_WARN_UNUSED_VARIABLE = YES; 392 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 393 | MTL_ENABLE_DEBUG_INFO = YES; 394 | ONLY_ACTIVE_ARCH = YES; 395 | SDKROOT = iphoneos; 396 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 397 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 398 | SWIFT_VERSION = 5.0; 399 | TARGETED_DEVICE_FAMILY = "1,2"; 400 | }; 401 | name = Debug; 402 | }; 403 | 956407DE1ECEF3AE00ED9239 /* Release */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ALWAYS_SEARCH_USER_PATHS = NO; 407 | CLANG_ANALYZER_NONNULL = YES; 408 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 410 | CLANG_CXX_LIBRARY = "libc++"; 411 | CLANG_ENABLE_MODULES = YES; 412 | CLANG_ENABLE_OBJC_ARC = YES; 413 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 414 | CLANG_WARN_BOOL_CONVERSION = YES; 415 | CLANG_WARN_COMMA = YES; 416 | CLANG_WARN_CONSTANT_CONVERSION = YES; 417 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 418 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 419 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 420 | CLANG_WARN_EMPTY_BODY = YES; 421 | CLANG_WARN_ENUM_CONVERSION = YES; 422 | CLANG_WARN_INFINITE_RECURSION = YES; 423 | CLANG_WARN_INT_CONVERSION = YES; 424 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 425 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 426 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 427 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 428 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 429 | CLANG_WARN_STRICT_PROTOTYPES = YES; 430 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 431 | CLANG_WARN_UNREACHABLE_CODE = YES; 432 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 433 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 434 | COPY_PHASE_STRIP = NO; 435 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 436 | ENABLE_NS_ASSERTIONS = NO; 437 | ENABLE_STRICT_OBJC_MSGSEND = YES; 438 | GCC_C_LANGUAGE_STANDARD = gnu99; 439 | GCC_NO_COMMON_BLOCKS = YES; 440 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 441 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 442 | GCC_WARN_UNDECLARED_SELECTOR = YES; 443 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 444 | GCC_WARN_UNUSED_FUNCTION = YES; 445 | GCC_WARN_UNUSED_VARIABLE = YES; 446 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 447 | MTL_ENABLE_DEBUG_INFO = NO; 448 | SDKROOT = iphoneos; 449 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 450 | SWIFT_VERSION = 5.0; 451 | TARGETED_DEVICE_FAMILY = "1,2"; 452 | VALIDATE_PRODUCT = YES; 453 | }; 454 | name = Release; 455 | }; 456 | 956407E01ECEF3AE00ED9239 /* Debug */ = { 457 | isa = XCBuildConfiguration; 458 | buildSettings = { 459 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | DEVELOPMENT_TEAM = 773A426V2P; 462 | INFOPLIST_FILE = SJLineRefresh/Info.plist; 463 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 464 | PRODUCT_BUNDLE_IDENTIFIER = com.shmily.test2; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | SWIFT_VERSION = 5.0; 467 | }; 468 | name = Debug; 469 | }; 470 | 956407E11ECEF3AE00ED9239 /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 474 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 475 | DEVELOPMENT_TEAM = 773A426V2P; 476 | INFOPLIST_FILE = SJLineRefresh/Info.plist; 477 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 478 | PRODUCT_BUNDLE_IDENTIFIER = com.shmily.test2; 479 | PRODUCT_NAME = "$(TARGET_NAME)"; 480 | SWIFT_VERSION = 5.0; 481 | }; 482 | name = Release; 483 | }; 484 | /* End XCBuildConfiguration section */ 485 | 486 | /* Begin XCConfigurationList section */ 487 | 956407C81ECEF3AE00ED9239 /* Build configuration list for PBXProject "SJLineRefresh" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 956407DD1ECEF3AE00ED9239 /* Debug */, 491 | 956407DE1ECEF3AE00ED9239 /* Release */, 492 | ); 493 | defaultConfigurationIsVisible = 0; 494 | defaultConfigurationName = Release; 495 | }; 496 | 956407DF1ECEF3AE00ED9239 /* Build configuration list for PBXNativeTarget "SJLineRefresh" */ = { 497 | isa = XCConfigurationList; 498 | buildConfigurations = ( 499 | 956407E01ECEF3AE00ED9239 /* Debug */, 500 | 956407E11ECEF3AE00ED9239 /* Release */, 501 | ); 502 | defaultConfigurationIsVisible = 0; 503 | defaultConfigurationName = Release; 504 | }; 505 | /* End XCConfigurationList section */ 506 | }; 507 | rootObject = 956407C51ECEF3AE00ED9239 /* Project object */; 508 | } 509 | -------------------------------------------------------------------------------- /SJLineRefresh.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SJLineRefresh.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SJLineRefresh/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/.DS_Store -------------------------------------------------------------------------------- /SJLineRefresh/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func applicationDidFinishLaunching(_ application: UIApplication) { 18 | 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/backImg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "hh_chat_pay_icon.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/backImg.imageset/hh_chat_pay_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Assets.xcassets/backImg.imageset/hh_chat_pay_icon.png -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/bg_pattern.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bg_pattern@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/bg_pattern.imageset/bg_pattern@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Assets.xcassets/bg_pattern.imageset/bg_pattern@2x.png -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/hh_refresh_center.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "hh_refresh_center.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/hh_refresh_center.imageset/hh_refresh_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Assets.xcassets/hh_refresh_center.imageset/hh_refresh_center.png -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/timg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "timg.jpg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SJLineRefresh/Assets.xcassets/timg.imageset/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Assets.xcassets/timg.imageset/timg.jpg -------------------------------------------------------------------------------- /SJLineRefresh/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 | -------------------------------------------------------------------------------- /SJLineRefresh/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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 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 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 269 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Demo/.DS_Store -------------------------------------------------------------------------------- /SJLineRefresh/Demo/Cell/SJSectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJSectionCell.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/20. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJSectionCell: UITableViewCell { 12 | 13 | @IBOutlet weak var titleLabel: UILabel! 14 | 15 | override func awakeFromNib() { 16 | 17 | super.awakeFromNib() 18 | 19 | contentView.backgroundColor = UIColor(patternImage: UIImage(named: "bg_pattern")!) 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/Cell/SJStyleCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJStyleCell.swift 3 | // SJLineRefresh 4 | // 5 | // Created by shiQ on 2017/6/30. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJStyleCell: UITableViewCell { 12 | 13 | @IBOutlet weak var segment: UISegmentedControl! 14 | 15 | var changeStyle:((SJAnimateStyle)->Void)? 16 | 17 | var defaultIndex = 0 { 18 | didSet { 19 | segment.selectedSegmentIndex = defaultIndex 20 | } 21 | } 22 | 23 | override func awakeFromNib() { 24 | super.awakeFromNib() 25 | 26 | let styles = ["reverse", "step", "stay", "normal"] 27 | 28 | segment.removeAllSegments() 29 | 30 | styles.forEach { 31 | self.segment.insertSegment(withTitle: $0, at: 0, animated: false) 32 | } 33 | 34 | } 35 | 36 | @IBAction func styleChange(_ sender: UISegmentedControl) { 37 | 38 | let aSelect = SJAnimateStyle(rawValue: sender.selectedSegmentIndex)! 39 | changeStyle?(aSelect) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/Cell/SJStyleCell.xib: -------------------------------------------------------------------------------- 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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/Demo+extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJRefreshView+ex.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension SJRefreshView { 12 | 13 | func reloadView() { 14 | 15 | config = SJRefreshManager.default.defaultConfig 16 | resetUI() 17 | } 18 | 19 | func change(config: SJRefreshConfig) { 20 | 21 | self.config = config 22 | resetUI() 23 | } 24 | } 25 | 26 | extension SJRefreshManager { 27 | 28 | func deomConfig() { 29 | let aPath = getCurrentBundle().path(forResource: "polygon", ofType: "plist") ?? "" 30 | SJRefreshManager.default.defaultConfig = SJRefreshConfig(plist: aPath).build { 31 | 32 | $0.lineColor = UIColor(red:47.0/255,green:148.0/255,blue:255.0/255,alpha:1) 33 | $0.backImg = getImage(name: "hh_refresh_center@3x") 34 | $0.centerOffset = CGPoint(x: 0.3, y: 0.5) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/SJBaseDemoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJBaseDemoController.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJBaseDemoController: UIViewController { 12 | 13 | var demoType = SJDemoType.polygon 14 | 15 | /// config 16 | var mConfig = SJRefreshConfig() 17 | 18 | @IBOutlet weak var tableView: UITableView! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | title = demoType.rawValue 24 | 25 | tableView.register(UINib(nibName: "SJTableViewCell", bundle: nil), forCellReuseIdentifier: "test") 26 | tableView.register(UINib(nibName: "SJStyleCell", bundle: nil), forCellReuseIdentifier: "segment") 27 | tableView.separatorStyle = .none 28 | } 29 | 30 | } 31 | 32 | // MARK: - tableView delegate and dataSource 33 | extension SJBaseDemoController: UITableViewDelegate, UITableViewDataSource { 34 | 35 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 36 | return 5 37 | } 38 | 39 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 40 | if indexPath.row == 0 { 41 | let aCell = tableView.dequeueReusableCell(withIdentifier: "segment", for: indexPath) as! SJStyleCell 42 | aCell.defaultIndex = mConfig.animConfig.style.rawValue 43 | 44 | aCell.changeStyle = { [weak self] in 45 | self?.tableView.sj_header?.config.animConfig.style = $0 46 | } 47 | return aCell 48 | } 49 | 50 | return tableView.dequeueReusableCell(withIdentifier: "test", for: indexPath) 51 | } 52 | 53 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 54 | return indexPath.row == 0 ? 40 : 300 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/SJDemoListController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJDemoListController.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/25. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | enum SJDemoType: String { 13 | 14 | case polygon = "polygon" 15 | case storehouse = "storehouse" 16 | case AKTA = "AKTA" 17 | case LOL = "LOL" 18 | case debug = "debug" 19 | 20 | case Other = "Other" 21 | 22 | static func enumAllValues() -> [SJDemoType] { 23 | return [.polygon, storehouse, .AKTA, .LOL, .Other] 24 | } 25 | 26 | func identifier() -> String { 27 | switch self { 28 | case .polygon: 29 | return "normal" 30 | 31 | case .AKTA: fallthrough 32 | case .storehouse: fallthrough 33 | case .Other: fallthrough 34 | case .LOL: 35 | return "demo" 36 | 37 | case .debug: 38 | return "jump2demo" 39 | } 40 | } 41 | 42 | } 43 | 44 | class SJDemoListController: UITableViewController { 45 | lazy var demoType = SJDemoType.polygon 46 | let demos = SJDemoType.enumAllValues() 47 | 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | tableView.tableFooterView = UIView() 51 | } 52 | 53 | override func numberOfSections(in tableView: UITableView) -> Int { 54 | return 2 55 | } 56 | 57 | //MARK: - delegate datasource 58 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 59 | return section == 0 ? demos.count : 1 60 | } 61 | 62 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 63 | let cell = tableView.dequeueReusableCell(withIdentifier: "test", for: indexPath) as! SJTestCell 64 | cell.centerLabel.text = demos[indexPath.row].rawValue 65 | 66 | return cell 67 | } 68 | 69 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 70 | demoType = demos[indexPath.row] 71 | let identifier = indexPath.section == 1 ? "jump2demo" : demoType.identifier() 72 | performSegue(withIdentifier: identifier, sender: self) 73 | } 74 | 75 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 76 | let destination = segue.destination as! SJBaseDemoController 77 | destination.demoType = demoType 78 | } 79 | 80 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 81 | return 75 82 | } 83 | 84 | override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 85 | let aView = tableView.dequeueReusableCell(withIdentifier: "section") as! SJSectionCell 86 | aView.titleLabel.text = section == 0 ? "demo mode" : "debug mode" 87 | 88 | return aView 89 | } 90 | 91 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 92 | return 45 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/SJDemoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJDemoViewController.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJDemoViewController: SJBaseDemoController { 12 | 13 | @IBOutlet var headerView: SJHeaderView! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | configRefresh() 19 | initHeader() 20 | startRefresh() 21 | } 22 | 23 | func configRefresh() { 24 | 25 | SJRefreshManager.default.deomConfig() 26 | tableView.sj_header = SJRefreshView.default { 27 | DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { 28 | self.refreshFinish() 29 | }) 30 | } 31 | } 32 | 33 | fileprivate func initHeader() { 34 | headerView.frame = headerView.getHeaderFrame(width: tableView.frame.sj_width) 35 | 36 | headerView.configChanged = { [weak self] in 37 | self?.tableView.sj_header?.reloadView() 38 | } 39 | headerView.addParaViews() 40 | 41 | tableView.tableHeaderView = headerView 42 | } 43 | 44 | fileprivate func startRefresh() { 45 | 46 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: { 47 | self.tableView.beginRefresh() 48 | }) 49 | } 50 | 51 | fileprivate func refreshFinish() { 52 | tableView.endRefresh() 53 | print("refresh success") 54 | } 55 | 56 | @IBAction func doReset(_ sender: UIButton) { 57 | SJRefreshManager.default.deomConfig() 58 | self.tableView.sj_header?.reloadView() 59 | 60 | headerView.resetParas() 61 | 62 | } 63 | 64 | @IBAction func doPrint(_ sender: UIButton) { 65 | headerView.configInfo.forEach{$0.printMe()} 66 | } 67 | } 68 | 69 | 70 | // MARK: - tableView delegate and dataSource 71 | extension SJDemoViewController { 72 | 73 | // section 74 | func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 75 | return tableView.dequeueReusableCell(withIdentifier: "section") 76 | } 77 | 78 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 79 | 80 | return 60 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/SJNormalUseController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJNormalUseController.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJNormalUseController: SJBaseDemoController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | let aPath = Bundle.main.path(forResource: "polygon", ofType: "plist")! 16 | mConfig = SJRefreshConfig(plist: aPath).build { 17 | 18 | $0.lineColor = UIColor(red:47.0/255,green:148.0/255,blue:255.0/255,alpha:1) 19 | $0.backImg = getImage(name: "hh_refresh_center@3x") 20 | $0.centerOffset = CGPoint(x: 0.3, y: 0.5) 21 | } 22 | 23 | tableView.sj_header = SJRefreshView(config: mConfig) { [weak self] in 24 | DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { 25 | self?.tableView.endRefresh() 26 | }) 27 | } 28 | } 29 | 30 | override func viewDidAppear(_ animated: Bool) { 31 | super.viewDidAppear(animated) 32 | tableView.beginRefresh() 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/SJStoreHouseController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJStoreHouseController.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/20. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJStoreHouseController: SJBaseDemoController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | tableView.backgroundColor = UIColor(patternImage: UIImage(named: "bg_pattern")!) 16 | 17 | setupRefresh() 18 | } 19 | 20 | override func viewDidAppear(_ animated: Bool) { 21 | super.viewDidAppear(animated) 22 | tableView.beginRefresh() 23 | } 24 | 25 | func setupRefresh() { 26 | let aPath = Bundle.main.path(forResource: demoType.rawValue, ofType: "plist")! 27 | mConfig = SJRefreshConfig(plist: aPath).build { 28 | 29 | $0.lineColor = UIColor.white 30 | $0.lineWidth = 2 31 | $0.dropHeight = 80 32 | $0.animConfig.randomness = 150 33 | $0.animConfig.stepDuration = 0.5 34 | } 35 | 36 | tableView.sj_header = SJRefreshView(config: mConfig, refresh: { [weak self] in 37 | DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: { 38 | self?.tableView.endRefresh() 39 | }) 40 | 41 | }) 42 | 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJConfigCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJConfigCell.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/2. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJConfigCell: UITableViewCell { 12 | 13 | @IBOutlet weak var configNameLabel: UILabel! 14 | @IBOutlet weak var configValueSlider: SJStepSlider! 15 | @IBOutlet weak var configValueLabel: UILabel! 16 | 17 | var configModel: SJConfigModel? { 18 | didSet { 19 | updateUI() 20 | } 21 | } 22 | 23 | override func awakeFromNib() { 24 | super.awakeFromNib() 25 | 26 | configValueSlider.changeBlock = { [weak self] value in 27 | 28 | self?.configModel?.changeValues(value: value) 29 | // self?.configModel?.hasChanged = true 30 | self?.configValueLabel.text = "\(value)" 31 | } 32 | } 33 | 34 | fileprivate func updateUI() { 35 | 36 | configNameLabel.text = configModel?.name 37 | 38 | configValueSlider.step = Float((configModel?.defaultVaule ?? 0) / 10) 39 | configValueSlider.maximumValue = Float(configModel?.defaultVaule ?? 0) * 2 40 | configValueLabel.text = "\(configModel?.defaultVaule ?? 0)" 41 | 42 | if configModel?.hasChanged ?? false { return } 43 | 44 | configValueSlider.value = Float(configModel?.defaultVaule ?? 0) 45 | } 46 | 47 | 48 | 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJConfigModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJConfigModel.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/2. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | 12 | enum HHConfigType: String { 13 | case darkAlpha = "darkAlpha" 14 | case lineWidth = "lineWidth" 15 | case dropHeight = "dropHeight" 16 | case startRatio = "startRatio" 17 | case endRatio = "endRatio" 18 | case randomness = "randomness" 19 | case disappearDuration = "disappearDuration" 20 | case animateFactor = "animateFactor" 21 | 22 | func changeValue(para: Float) { 23 | 24 | let paraValue = CGFloat(para) 25 | 26 | switch self { 27 | case .darkAlpha: 28 | SJRefreshManager.default.defaultConfig.darkAlpha = paraValue 29 | 30 | case .lineWidth: 31 | SJRefreshManager.default.defaultConfig.lineWidth = paraValue 32 | 33 | case .dropHeight: 34 | SJRefreshManager.default.defaultConfig.dropHeight = paraValue 35 | 36 | case .startRatio: 37 | SJRefreshManager.default.defaultConfig.animConfig.startRatio = paraValue 38 | 39 | case .endRatio: 40 | SJRefreshManager.default.defaultConfig.animConfig.endRatio = paraValue 41 | 42 | case .randomness: 43 | SJRefreshManager.default.defaultConfig.animConfig.randomness = Int(paraValue) 44 | 45 | case .disappearDuration: 46 | 47 | SJRefreshManager.default.defaultConfig.animConfig.animeDuration = Double(paraValue) 48 | 49 | case .animateFactor: 50 | SJRefreshManager.default.defaultConfig.animConfig.animateFactor = paraValue 51 | 52 | // default: 53 | // 54 | // break 55 | } 56 | } 57 | } 58 | 59 | class SJConfigModel { 60 | 61 | var name: String? 62 | 63 | var type: HHConfigType? 64 | 65 | var defaultVaule: CGFloat? 66 | 67 | var hasChanged = false 68 | 69 | init(type: HHConfigType, defaultVaule: CGFloat?) { 70 | self.type = type 71 | self.name = type.rawValue 72 | self.defaultVaule = defaultVaule 73 | } 74 | 75 | func printMe() -> Void { 76 | 77 | let aResult = (name ?? "") + ": " + "\(defaultVaule ?? 0)" 78 | print(aResult) 79 | } 80 | 81 | static func `default`() -> [SJConfigModel] { 82 | 83 | var defaults = [SJConfigModel]() 84 | let aConfig = SJRefreshConfig() 85 | 86 | let aDark = SJConfigModel(type: HHConfigType.darkAlpha, defaultVaule: aConfig.darkAlpha) 87 | defaults.append(aDark) 88 | 89 | let aLineWidth = SJConfigModel(type: HHConfigType.lineWidth, defaultVaule: aConfig.lineWidth) 90 | defaults.append(aLineWidth) 91 | 92 | let aDropHeight = SJConfigModel(type: HHConfigType.dropHeight, defaultVaule: aConfig.dropHeight) 93 | defaults.append(aDropHeight) 94 | 95 | 96 | let aStartRatio = SJConfigModel(type: HHConfigType.startRatio, defaultVaule: aConfig.animConfig.startRatio) 97 | defaults.append(aStartRatio) 98 | 99 | let aEndRatio = SJConfigModel(type: HHConfigType.endRatio, defaultVaule: aConfig.animConfig.endRatio) 100 | defaults.append(aEndRatio) 101 | 102 | let aRandom = SJConfigModel(type: HHConfigType.randomness, defaultVaule: CGFloat(aConfig.animConfig.randomness)) 103 | defaults.append(aRandom) 104 | 105 | let aDisappearDuration = SJConfigModel(type: HHConfigType.disappearDuration, defaultVaule: CGFloat(aConfig.animConfig.animeDuration)) 106 | defaults.append(aDisappearDuration) 107 | 108 | let aAnimateFactor = SJConfigModel(type: HHConfigType.animateFactor, defaultVaule: aConfig.animConfig.animateFactor) 109 | defaults.append(aAnimateFactor) 110 | 111 | return defaults 112 | } 113 | 114 | func changeValues(value: Float) { 115 | 116 | hasChanged = true 117 | 118 | type?.changeValue(para: value) 119 | 120 | 121 | 122 | } 123 | 124 | } 125 | 126 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJConfigSectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJConfigSectionCell.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/2. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJConfigSectionCell: UITableViewCell { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJConfigView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJConfigView.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/5. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJConfigView: UIView { 12 | 13 | @IBOutlet weak var configNameLabel: UILabel! 14 | @IBOutlet weak var configValueSlider: SJStepSlider! 15 | @IBOutlet weak var configValueLabel: UILabel! 16 | 17 | var mValueChanged: (()->Void)? 18 | 19 | var configModel: SJConfigModel? { 20 | didSet { 21 | updateUI() 22 | } 23 | } 24 | 25 | init(config: SJConfigModel) { 26 | 27 | super.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.sj_width, height: 40)) 28 | addChildViewFromNib() 29 | 30 | self.configModel = config 31 | updateUI() 32 | 33 | configValueSlider.changeBlock = { [weak self] value in 34 | 35 | self?.configModel?.changeValues(value: value) 36 | self?.configValueLabel.text = "\(value)" 37 | 38 | self?.mValueChanged?() 39 | } 40 | } 41 | 42 | override init(frame: CGRect) { 43 | super.init(frame: frame) 44 | addChildViewFromNib() 45 | } 46 | 47 | required init?(coder aDecoder: NSCoder) { 48 | super.init(coder: aDecoder) 49 | 50 | addChildViewFromNib() 51 | } 52 | 53 | func addChildViewFromNib() 54 | { 55 | let subView:UIView = UINib(nibName:"SJConfigView", bundle: Bundle(for:SJConfigView.self)).instantiate(withOwner: self, options: nil)[0] as! UIView 56 | subView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height) 57 | 58 | addSubview(subView) 59 | 60 | } 61 | 62 | func updateUI() { 63 | 64 | configNameLabel.text = configModel?.name 65 | 66 | configValueSlider.step = Float((configModel?.defaultVaule ?? 0) / 10) 67 | configValueSlider.maximumValue = Float(configModel?.defaultVaule ?? 0) * 4 68 | configValueLabel.text = "\(configModel?.defaultVaule ?? 0)" 69 | 70 | if configModel?.hasChanged ?? false { return } 71 | 72 | configValueSlider.value = Float(configModel?.defaultVaule ?? 0) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJConfigView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJHeaderView.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/5. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJHeaderView: UIView { 12 | 13 | @IBOutlet weak var stackView: UIStackView! 14 | 15 | var configInfo = SJConfigModel.default() 16 | 17 | var configChanged: (()->Void)? 18 | 19 | func addParaViews() { 20 | 21 | for aInfo in configInfo { 22 | 23 | let aView = SJConfigView(config: aInfo) 24 | aView.mValueChanged = configChanged 25 | 26 | stackView.addSubview(aView) 27 | stackView.addArrangedSubview(aView) 28 | } 29 | } 30 | 31 | func resetParas() { 32 | 33 | configInfo = SJConfigModel.default() 34 | 35 | var i = 0 36 | for aView in stackView.arrangedSubviews { 37 | 38 | (aView as? SJConfigView)?.configModel = configInfo[i] 39 | i += 1 40 | } 41 | 42 | } 43 | 44 | 45 | func getHeaderFrame(width: CGFloat) -> CGRect { 46 | 47 | return CGRect(x: 0, y: 0, width: Int(width), height: 45 * configInfo.count) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJStepSlider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJStepSlider.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/2. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJStepSlider: UISlider { 12 | 13 | @IBInspectable var step: Float = 0.1 14 | 15 | var changeBlock: ((Float) -> Void)? 16 | 17 | required init?(coder aDecoder: NSCoder) { 18 | 19 | super.init(coder: aDecoder) 20 | 21 | addTarget() 22 | } 23 | 24 | override init(frame: CGRect) { 25 | 26 | super.init(frame: frame) 27 | addTarget() 28 | } 29 | 30 | fileprivate func addTarget() { 31 | 32 | self.isContinuous = false 33 | 34 | addTarget(self, action: #selector(changeSlider), for: UIControl.Event.valueChanged) 35 | } 36 | 37 | @objc func changeSlider() { 38 | 39 | self.setValue(getRealValue(setted: self.value), animated: true) 40 | 41 | changeBlock?(self.value) 42 | } 43 | 44 | func getRealValue(setted: Float) -> Float { 45 | 46 | let multiple = Int(setted / step) 47 | let moreHalf = (setted - step * Float(multiple)) > (step / 2) 48 | 49 | return step * Float(multiple + (moreHalf ? 1 : 0)) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJTableViewCell.xib: -------------------------------------------------------------------------------- 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 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SJLineRefresh/Demo/View/SJTestCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJTestCell.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/6/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SJTestCell: UITableViewCell { 12 | 13 | @IBOutlet weak var centerLabel: UILabel! 14 | 15 | 16 | override func awakeFromNib() { 17 | super.awakeFromNib() 18 | // Initialization code 19 | } 20 | 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /SJLineRefresh/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SJLineRefresh/Resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Resources/.DS_Store -------------------------------------------------------------------------------- /SJLineRefresh/Resources/AKTA.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | startPoints 6 | 7 | {22,0} 8 | {22,0} 9 | {30,0} 10 | {0,30} 11 | {60,0} 12 | {60,15} 13 | {60,15} 14 | {85,15} 15 | {85,15} 16 | {117,0} 17 | {147,0} 18 | {147,0} 19 | {198,0} 20 | {198,0} 21 | {206,0} 22 | {176,30} 23 | 24 | endPoints 25 | 26 | {0,30} 27 | {30,0} 28 | {52,30} 29 | {26,30} 30 | {60,15} 31 | {60,30} 32 | {85,15} 33 | {108,0} 34 | {108,30} 35 | {147,0} 36 | {177,0} 37 | {147,32} 38 | {176,30} 39 | {206,0} 40 | {228,30} 41 | {202,30} 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /SJLineRefresh/Resources/LOL.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | startPoints 6 | 7 | {1,8} 8 | {1,29} 9 | {1,50} 10 | 11 | {52,7} 12 | {52,27} 13 | {52,51} 14 | {75,51} 15 | {75,27} 16 | {75,7} 17 | 18 | {112,9} 19 | {112,30} 20 | {112,51} 21 | 22 | endPoints 23 | 24 | {1,29} 25 | {1,50} 26 | {26,50} 27 | 28 | {52,27} 29 | {52,51} 30 | {75,51} 31 | {75,27} 32 | {75,7} 33 | {52,7} 34 | 35 | {112,30} 36 | {112,51} 37 | {137,51} 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SJLineRefresh/Resources/Other.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | startPoints 6 | 7 | {22,0} 8 | {43.65,12.5} 9 | {43.65,37.5} 10 | {22,50} 11 | {1, 37.5} 12 | {1,12.5} 13 | {11.5, 14.5} 14 | {21.5, 21} 15 | {9, 25} 16 | {18,29} 17 | {33,25} 18 | {24,29} 19 | 20 | endPoints 21 | 22 | {43.65,12.5} 23 | {43.65,37.5} 24 | {22,50} 25 | {1,37.5} 26 | {1,12.5} 27 | {22,0} 28 | {21.5, 21} 29 | {33.5, 14.5} 30 | {18,29} 31 | {18, 40} 32 | {24,29} 33 | {24,40} 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /SJLineRefresh/Resources/SJLineRefresh.bundle/hh_refresh_center@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/SJLineRefresh/Resources/SJLineRefresh.bundle/hh_refresh_center@3x.png -------------------------------------------------------------------------------- /SJLineRefresh/Resources/polygon.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | startPoints 6 | 7 | {16,1.25} 8 | {30.51,9.62} 9 | {30.51,26.37} 10 | {16,34.75} 11 | {1.49, 26.38} 12 | {1.49, 9.63} 13 | {16,1.25} 14 | 15 | endPoints 16 | 17 | {30.51,9.62} 18 | {30.51,26.37} 19 | {16,34.75} 20 | {1.49, 26.38} 21 | {1.49, 9.63} 22 | {16,1.25} 23 | {16,1.25} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SJLineRefresh/Resources/storehouse.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 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJAnimateConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJAnimateConfig.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/20. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct SJAnimateConfig { 12 | 13 | public var startRatio: CGFloat = 0.4 14 | 15 | public var endRatio: CGFloat = 1 16 | 17 | // second 18 | public var stepLength: TimeInterval = 0.1 19 | 20 | public var style = SJAnimateStyle.reverse 21 | 22 | /// step duration 0.8 23 | public var stepDuration: Double = 0.8 24 | 25 | public var animeDuration:TimeInterval = 0.5 26 | 27 | public var animateFactor: CGFloat = 0.9 28 | 29 | /// need dismiss animation or not 30 | public var isDismissAnime: Bool = true 31 | 32 | public var randomness: Int = 150 { 33 | didSet { 34 | if randomness <= 0 { 35 | randomness = 1 36 | } 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJConstants.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/20. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | import UIKit 9 | 10 | let kStartPoints = "startPoints" 11 | let kEndPoints = "endPoints" 12 | let kScrollviewContentPffset = "contentOffset" 13 | 14 | public enum SJRefreshState: Int { 15 | 16 | case idle = 0 17 | case pulling 18 | case willRefresh 19 | case refresing 20 | case finish 21 | } 22 | 23 | 24 | /// animation style 25 | /// 26 | /// - normal: default 27 | /// - stay: flash will not disappear 28 | /// - step: add line step by step 29 | /// - reverse: reverse loading direction 30 | public enum SJAnimateStyle: Int { 31 | 32 | case normal = 0 33 | case stay 34 | case step 35 | case reverse 36 | 37 | func isStep() -> Bool { 38 | 39 | return self == .step 40 | } 41 | 42 | static func allStyles() -> [SJAnimateStyle] { 43 | return [.reverse, .normal, .step, .stay] 44 | } 45 | } 46 | 47 | 48 | // 获取当前组件的bundle 49 | func getCurrentBundle() -> Bundle { 50 | return Bundle(for: SJRefreshView.self) 51 | } 52 | 53 | func getResourceBundle() -> Bundle { 54 | let aURl = getCurrentBundle().url(forResource: "SJLineRefresh", withExtension: "bundle")! 55 | 56 | return Bundle(url: aURl)! 57 | } 58 | 59 | func getImage(name: String) -> UIImage { 60 | guard let aPath = getResourceBundle().path(forResource: name, ofType: "png") else { return UIImage() } 61 | return UIImage(contentsOfFile: aPath) ?? UIImage() 62 | } 63 | 64 | extension CGRect { 65 | 66 | var sj_width: CGFloat { 67 | get { 68 | return size.width 69 | } 70 | set { 71 | size.width = newValue 72 | } 73 | } 74 | 75 | var sj_height: CGFloat { 76 | get { 77 | return size.height 78 | } 79 | set { 80 | size.height = newValue 81 | } 82 | } 83 | 84 | var sj_x: CGFloat { 85 | get { 86 | return origin.x 87 | } 88 | 89 | set { 90 | origin.x = newValue 91 | } 92 | } 93 | 94 | var sj_y: CGFloat { 95 | get { 96 | return origin.y 97 | } 98 | 99 | set { 100 | origin.y = newValue 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJLinePathView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJLinePathView.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// a line from start point to end point 12 | public class SJLinePathView: UIView { 13 | 14 | public lazy var config = SJRefreshConfig() 15 | public var translationX: CGFloat = 0 16 | 17 | override public func draw(_ rect: CGRect) { 18 | let aPath = UIBezierPath() 19 | aPath.move(to: config.startPoint) 20 | aPath.addLine(to: config.endPoint) 21 | config.lineColor?.setStroke() 22 | aPath.lineWidth = config.lineWidth 23 | // aPath.lineCapStyle = .square 24 | aPath.stroke() 25 | 26 | } 27 | 28 | public init(frame: CGRect, config: SJRefreshConfig) { 29 | super.init(frame: frame) 30 | self.config = config 31 | } 32 | 33 | required public init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | } 36 | 37 | func setUp() { 38 | layer.anchorPoint = CGPoint(x: config.middlePoint.x / frame.size.width, y: config.middlePoint.y / frame.size.height) 39 | frame = CGRect(x: frame.sj_x + config.middlePoint.x - frame.sj_width / 2, y: frame.sj_y + config.middlePoint.y - frame.sj_height / 2, width: frame.sj_width, height: frame.sj_height) 40 | } 41 | 42 | func setRadom() { 43 | let random = Int(arc4random_uniform(UInt32(config.animConfig.randomness)) * 2) 44 | translationX = CGFloat(-config.animConfig.randomness + random) 45 | transform = CGAffineTransform(translationX: translationX, y: -config.dropHeight) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJRefreshConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJRefreshConfig.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum SJLoadStyle: Int { 12 | 13 | case step 14 | case collect 15 | } 16 | 17 | /// refresh parameters 18 | public struct SJRefreshConfig { 19 | 20 | /// animation configuration parameter 21 | public var animConfig = SJAnimateConfig() 22 | 23 | /// scrollview drop down height 24 | public var dropHeight: CGFloat = 80 25 | 26 | /// pathView's center offset 27 | public var centerOffset = CGPoint.zero 28 | 29 | /// path for plist file with line points 30 | public var plistPath = "" 31 | 32 | /// default path alpha 33 | public var darkAlpha: CGFloat = 0.25 34 | 35 | /// scale 36 | public var scale: CGFloat = 1 37 | 38 | public var backImg: UIImage? 39 | 40 | //MARK: - path's parameter 41 | 42 | /// path's line width 43 | public var lineWidth: CGFloat = 0.5 44 | 45 | /// path's line Color 46 | public var lineColor: UIColor? 47 | 48 | /// path's line start point 49 | var startPoint = CGPoint.zero 50 | 51 | /// path's line end point 52 | var endPoint = CGPoint.zero { 53 | didSet { 54 | middlePoint = CGPoint(x: (startPoint.x + endPoint.x) / 2, y: (startPoint.y + endPoint.y) / 2) 55 | } 56 | } 57 | 58 | /// pathView.s line middle point 59 | var middlePoint = CGPoint.zero 60 | 61 | public init() { 62 | } 63 | 64 | /// init 65 | /// 66 | /// - Parameter plist: path for plist file with line points 67 | public init(plist: String) { 68 | plistPath = plist 69 | } 70 | 71 | /// config refresh parameters 72 | /// 73 | /// - Parameter block: $0.parameter = value 74 | /// - Returns: SJRefreshConfig 75 | public func build(_ block: (inout SJRefreshConfig) -> Void) -> SJRefreshConfig { 76 | var copy = self 77 | block(©) 78 | return copy 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJRefreshManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJRefreshManager.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/22. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class SJRefreshManager { 12 | 13 | public static let `default` = SJRefreshManager() 14 | 15 | public var startPoints: [String]? 16 | 17 | public var endPoints: [String]? 18 | 19 | public var plistPath: String? 20 | 21 | public var defaultConfig = SJRefreshConfig() 22 | 23 | public func fetchPoints(path: String) -> ([String]?, [String]?) { 24 | if plistPath != path { 25 | let aDict = NSDictionary.init(contentsOfFile: path) 26 | startPoints = aDict?.object(forKey: kStartPoints) as? [String] 27 | endPoints = aDict?.object(forKey: kEndPoints) as? [String] 28 | plistPath = path 29 | } 30 | 31 | return (startPoints, endPoints) 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJRefreshView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJRefreshView.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/19. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// refresh view 12 | public class SJRefreshView: UIView { 13 | 14 | var refreshBlock: (()->Void)? 15 | 16 | var config = SJRefreshConfig() 17 | 18 | fileprivate var pullingPercent: CGFloat? 19 | fileprivate var insetTDelta: CGFloat = 0 20 | fileprivate var scrollView: UIScrollView? 21 | 22 | lazy fileprivate var pathViews = [SJLinePathView]() 23 | lazy fileprivate var originalInset = UIEdgeInsets.zero 24 | 25 | fileprivate var timer: Timer? 26 | fileprivate var currentIndex: Int = 0 27 | 28 | var state = SJRefreshState.idle { 29 | didSet { 30 | refreshStateChange(old: oldValue) 31 | } 32 | } 33 | 34 | public class func `default`(refreshBlock: @escaping (()->Void)) -> SJRefreshView { 35 | return SJRefreshView(config: SJRefreshManager.default.defaultConfig, refresh: refreshBlock) 36 | } 37 | 38 | public init(config: SJRefreshConfig, refresh: @escaping ()->Void) { 39 | super.init(frame: CGRect.zero) 40 | self.config = config 41 | self.refreshBlock = refresh 42 | } 43 | 44 | public override init(frame: CGRect) { 45 | super.init(frame: frame) 46 | self.state = .idle 47 | } 48 | 49 | required public init?(coder aDecoder: NSCoder) { 50 | super.init(coder: aDecoder) 51 | } 52 | 53 | public override func draw(_ rect: CGRect) { 54 | super.draw(rect) 55 | 56 | if state == .willRefresh { 57 | state = .refresing 58 | } 59 | } 60 | 61 | public override func layoutSubviews() { 62 | super.layoutSubviews() 63 | guard let aScroll = scrollView else { return } 64 | center = CGPoint(x: aScroll.bounds.size.width / 2 + config.centerOffset.x, y: -config.dropHeight / 2 + config.centerOffset.y) 65 | } 66 | 67 | /// 刷新状态改变 68 | func refreshStateChange(old: SJRefreshState) { 69 | if old == state { return } 70 | guard let aScrollView = scrollView else { return } 71 | DispatchQueue.main.async { 72 | self.setNeedsLayout() 73 | } 74 | 75 | if state == .idle { 76 | if old != .refresing { return } 77 | // 恢复inset 78 | UIView.animate(withDuration: config.animConfig.animeDuration, animations: { 79 | var aInset = aScrollView.contentInset 80 | aInset.top += self.insetTDelta 81 | aScrollView.contentInset = aInset 82 | 83 | }, completion: { _ in 84 | self.pullingPercent = 0 85 | self.endTimer() 86 | }) 87 | 88 | self.startFinishAnime() 89 | return 90 | } 91 | 92 | if state == .refresing { 93 | DispatchQueue.main.async { 94 | UIView.animate(withDuration: self.config.animConfig.animeDuration, animations: { 95 | let aTop = self.originalInset.top + self.config.dropHeight 96 | var aInset = aScrollView.contentInset 97 | aInset.top = aTop 98 | aScrollView.contentInset = aInset 99 | self.scrollView?.setContentOffset(CGPoint(x: 0, y: -aTop), animated: false) 100 | }, completion: { [weak self] (finish) in 101 | self?.refreshBlock?() 102 | }) 103 | } 104 | 105 | startAniam() 106 | } 107 | } 108 | } 109 | 110 | 111 | //MARK: - public func 112 | extension SJRefreshView { 113 | 114 | public func beginRefresh() { 115 | pullingPercent = 1 116 | updatePathView() 117 | if self.window != nil { 118 | state = .refresing 119 | } else { 120 | if state != .refresing { 121 | state = .willRefresh 122 | setNeedsDisplay() 123 | } 124 | } 125 | } 126 | 127 | public func endRefresh() { 128 | DispatchQueue.main.async { 129 | self.state = .idle 130 | self.pathViews.forEach { [weak self] (aPathView) in 131 | aPathView.layer.removeAllAnimations() 132 | aPathView.alpha = self?.config.darkAlpha ?? 0 133 | } 134 | } 135 | } 136 | 137 | public func resetUI() { 138 | for aView in self.subviews { 139 | aView.removeFromSuperview() 140 | } 141 | 142 | pathViews = [SJLinePathView]() 143 | initUI() 144 | } 145 | } 146 | 147 | extension SJRefreshView { 148 | 149 | fileprivate func initUI() { 150 | backgroundColor = UIColor.clear 151 | guard let aScroll = scrollView else { return } 152 | let points = SJRefreshManager.default.fetchPoints(path: config.plistPath) 153 | guard let aStarts = points.0, let aEnds = points.1 else { return } 154 | 155 | if aEnds.count != aStarts.count { 156 | print("para count must is equal") 157 | return 158 | } 159 | 160 | var width: CGFloat = 0 161 | var height: CGFloat = 0 162 | 163 | for i in 0..= 1 - endPadding { 244 | aPathView.transform = .identity 245 | aPathView.alpha = config.darkAlpha 246 | 247 | } else if aPercent == 0 { 248 | aPathView.setRadom() 249 | } else { 250 | let aProgress = aPercent <= startPadding ? 0 : min(1, (aPercent - startPadding) / factor) 251 | aPathView.transform = CGAffineTransform(translationX: aPathView.translationX * (1 - aProgress), y: config.dropHeight * (1 - aProgress)) 252 | 253 | aPathView.transform = aPathView.transform.rotated(by: CGFloat(Double.pi) * aProgress) 254 | aPathView.transform = aPathView.transform.scaledBy(x: aProgress, y: aProgress) 255 | aPathView.alpha = aProgress * config.darkAlpha 256 | } 257 | } 258 | } 259 | 260 | /// make line path flashed 261 | @objc func flashPath() { 262 | if state != .refresing { return } 263 | 264 | if currentIndex >= pathViews.count { 265 | currentIndex = 0 266 | } 267 | 268 | let aIndex = config.animConfig.style == .reverse ? (pathViews.count - 1 - currentIndex) : currentIndex 269 | let view = pathViews[aIndex] 270 | 271 | currentIndex += 1 272 | 273 | view.layer.removeAllAnimations() 274 | 275 | stepAnimation(view: view) 276 | 277 | if (aIndex == pathViews.count - 1) && state == .refresing { 278 | cycleAnimation() 279 | } 280 | } 281 | 282 | @objc func finishAnime() { 283 | pullingPercent = (pullingPercent ?? 1) - CGFloat(1 / 60 / config.animConfig.animeDuration) 284 | updatePathView() 285 | } 286 | 287 | func stepAnimation(view: SJLinePathView) { 288 | switch config.animConfig.style { 289 | case .stay: 290 | stayStyle(view) 291 | 292 | case .normal: fallthrough 293 | case .step: fallthrough 294 | case .reverse: 295 | normalStyle(view) 296 | } 297 | } 298 | 299 | func cycleAnimation() { 300 | switch config.animConfig.style { 301 | case .stay: 302 | statyCycle() 303 | 304 | case .normal: fallthrough 305 | case .step: fallthrough 306 | case .reverse: 307 | normalCycle() 308 | } 309 | } 310 | 311 | } 312 | 313 | // MARK: - style 314 | extension SJRefreshView { 315 | 316 | func stayStyle(_ view: SJLinePathView) { 317 | view.alpha = config.darkAlpha 318 | UIView.animate(withDuration: config.animConfig.stepDuration, animations: { 319 | view.alpha = 1 320 | }) 321 | } 322 | 323 | func normalStyle(_ view: SJLinePathView) { 324 | view.alpha = 1 325 | UIView.animate(withDuration: config.animConfig.stepDuration, animations: { 326 | view.alpha = self.config.darkAlpha 327 | }) 328 | } 329 | 330 | func statyCycle() { 331 | UIView.animate(withDuration: 0.3, animations: { 332 | self.pathViews.forEach{$0.alpha = self.config.darkAlpha} 333 | }) { (finish) in 334 | self.startAniam() 335 | } 336 | } 337 | 338 | func normalCycle() { 339 | startAniam() 340 | } 341 | } 342 | 343 | // MARK: - timer 344 | extension SJRefreshView { 345 | 346 | func startAniam() { 347 | timer?.invalidate() 348 | timer = Timer.scheduledTimer(timeInterval: config.animConfig.stepLength, target: self, selector: #selector(flashPath), userInfo: nil, repeats: true) 349 | } 350 | 351 | func startFinishAnime() { 352 | timer?.invalidate() 353 | if !config.animConfig.isDismissAnime { return } 354 | 355 | timer = Timer.scheduledTimer(timeInterval: 1 / 60, target: self, selector: #selector(finishAnime), userInfo: nil, repeats: true) 356 | pullingPercent = 1 357 | } 358 | 359 | func endTimer() { 360 | currentIndex = 0 361 | timer?.invalidate() 362 | timer = nil 363 | } 364 | } 365 | 366 | // MARK: - observer 367 | extension SJRefreshView { 368 | 369 | func addObserver() { 370 | let aOption: NSKeyValueObservingOptions = [.new, .old] 371 | scrollView?.addObserver(self, forKeyPath: kScrollviewContentPffset, options: aOption, context: nil) 372 | } 373 | 374 | func removeObserver() { 375 | superview?.removeObserver(self, forKeyPath: kScrollviewContentPffset) 376 | } 377 | 378 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 379 | if !isUserInteractionEnabled || isHidden { return } 380 | if keyPath == kScrollviewContentPffset { 381 | contentOffsetChanged(change: change) 382 | } 383 | } 384 | 385 | func contentOffsetChanged(change: [NSKeyValueChangeKey : Any]?) { 386 | guard let aScrollView = scrollView else { return } 387 | if state == .refresing { 388 | if window == nil { return } 389 | var insetTop = max(-aScrollView.contentOffset.y, originalInset.top) 390 | insetTop = min(config.dropHeight + originalInset.top, insetTop) 391 | 392 | var aInset = aScrollView.contentInset 393 | aInset.top = insetTop 394 | scrollView?.contentInset = aInset 395 | 396 | insetTDelta = originalInset.top - insetTop 397 | return 398 | } 399 | 400 | originalInset = aScrollView.contentInset 401 | 402 | let currentOffY = aScrollView.contentOffset.y 403 | let happendOffY = -originalInset.top 404 | if currentOffY > happendOffY { return } // 向上滚动到看不见头部控件 405 | 406 | let normalOffY = happendOffY - config.dropHeight 407 | let pullingPercent = (happendOffY - currentOffY) / config.dropHeight 408 | 409 | if aScrollView.isDragging { // dragging 410 | redict(pullingPercent: pullingPercent) 411 | if state == .idle { 412 | updatePathView() 413 | if currentOffY < normalOffY { state = .pulling } // will refresh 414 | 415 | } else if state == .pulling && currentOffY >= normalOffY { // normal 416 | state = .idle 417 | } 418 | 419 | } else if state == .pulling { // no in hand & will refresh 420 | beginRefresh() 421 | 422 | } else if pullingPercent < 1 { 423 | self.pullingPercent = pullingPercent 424 | // reset refresh line 425 | self.pathViews.forEach { $0.alpha = 0 } 426 | 427 | } 428 | } 429 | 430 | // redic pulling percent 431 | fileprivate func redict(pullingPercent: CGFloat) { 432 | if pullingPercent <= config.animConfig.startRatio { 433 | self.pullingPercent = 0 434 | 435 | } else if pullingPercent >= config.animConfig.endRatio { 436 | self.pullingPercent = 1 437 | 438 | } else { 439 | self.pullingPercent = (pullingPercent - config.animConfig.startRatio) / (config.animConfig.endRatio - pullingPercent) 440 | } 441 | } 442 | 443 | } 444 | -------------------------------------------------------------------------------- /SJLineRefresh/Sources/SJScrollViewExtention.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SJSCrollViewExtention.swift 3 | // SJLineRefresh 4 | // 5 | // Created by Shi Jian on 2017/5/20. 6 | // Copyright © 2017年 Shi Jian. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private var key: Void? 12 | 13 | public extension UIScrollView { 14 | 15 | var sj_header: SJRefreshView? { 16 | get { 17 | return objc_getAssociatedObject(self, &key) as? SJRefreshView 18 | } 19 | 20 | set { 21 | objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 22 | sj_header?.removeFromSuperview() 23 | if let aHeader = sj_header { 24 | self.insertSubview(aHeader, at: 0) 25 | } 26 | } 27 | } 28 | 29 | func beginRefresh() { 30 | sj_header?.beginRefresh() 31 | } 32 | 33 | func endRefresh() { 34 | sj_header?.endRefresh() 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /imgs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/.DS_Store -------------------------------------------------------------------------------- /imgs/AKTA.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/AKTA.gif -------------------------------------------------------------------------------- /imgs/LOL.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/LOL.gif -------------------------------------------------------------------------------- /imgs/SJLineRefresh-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/SJLineRefresh-icon.png -------------------------------------------------------------------------------- /imgs/debug.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/debug.gif -------------------------------------------------------------------------------- /imgs/paintCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/paintCode.png -------------------------------------------------------------------------------- /imgs/polygon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/polygon.gif -------------------------------------------------------------------------------- /imgs/stay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/stay.gif -------------------------------------------------------------------------------- /imgs/step.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashen-23/SJLineRefresh/caa72ebc73f7e0177e822e61783911940b4dd059/imgs/step.gif --------------------------------------------------------------------------------