├── RefreshControlDemo ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard ├── Default.png ├── Default@2x.png ├── Default-568h@2x.png ├── AppearanceCustomizationController.h ├── RefreshControlDemo-Prefix.pch ├── ColorRefreshController.h ├── DefaultRefreshController.h ├── StoryboardViewController.h ├── TextRefreshingController.h ├── AppDelegate.h ├── main.m ├── RefreshControlDemo-Info.plist ├── AppearanceCustomizationController.m ├── DefaultRefreshController.m ├── TextRefreshingController.m ├── ColorRefreshController.m ├── AppDelegate.m └── StoryboardViewController.m ├── CKRefreshControl ├── CKRefreshControl-Prefix.pch ├── CKRefreshArrowView.h ├── CKParagraphStyle.h ├── CKParagraphStyle.m ├── CKRefreshControl.h ├── CKRefreshArrowView.m └── CKRefreshControl.m ├── CKRefreshControl.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── RefreshControlDemo.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── CKRefreshControl.podspec ├── LICENSE.txt ├── README.md └── .gitignore /RefreshControlDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /RefreshControlDemo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructure/CKRefreshControl/HEAD/RefreshControlDemo/Default.png -------------------------------------------------------------------------------- /RefreshControlDemo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructure/CKRefreshControl/HEAD/RefreshControlDemo/Default@2x.png -------------------------------------------------------------------------------- /RefreshControlDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructure/CKRefreshControl/HEAD/RefreshControlDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /CKRefreshControl/CKRefreshControl-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CKRefreshControl' target in the 'CKRefreshControl' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /CKRefreshControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RefreshControlDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RefreshControlDemo/AppearanceCustomizationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppearanceCustomizationController.h 3 | // RefreshControlDemo 4 | // 5 | // Created by BJ Homer on 10/22/12. 6 | // Copyright (c) 2012 Instructure, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppearanceCustomizationController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /RefreshControlDemo/RefreshControlDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'RefreshControlDemo' target in the 'RefreshControlDemo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /CKRefreshControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "CKRefreshControl" 3 | s.version = "1.1.1" 4 | s.summary = "A pull-to-refresh view for iOS 5, 100% API-compatible with UIRefreshControl in iOS 6." 5 | s.homepage = "https://github.com/instructure/CKRefreshControl" 6 | s.license = 'MIT' 7 | s.author = 'Instructure, Inc.' 8 | s.source = { :git => "https://github.com/instructure/CKRefreshControl.git", :tag => "1.1.1" } 9 | s.platform = :ios, '5.0' 10 | s.source_files = 'CKRefreshControl/' 11 | s.public_header_files = 'CKRefreshControl/CKRefreshControl.h' 12 | s.framework = 'QuartzCore' 13 | s.requires_arc = true 14 | s.xcconfig = { "OTHER_LDFLAGS" => "-ObjC" } 15 | end -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /RefreshControlDemo/ColorRefreshController.h: -------------------------------------------------------------------------------- 1 | // ColorRefreshController.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface ColorRefreshController : UITableViewController 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /RefreshControlDemo/DefaultRefreshController.h: -------------------------------------------------------------------------------- 1 | // DefaultRefreshController.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface DefaultRefreshController : UITableViewController 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /RefreshControlDemo/StoryboardViewController.h: -------------------------------------------------------------------------------- 1 | // StoryboardViewController.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface StoryboardViewController : UITableViewController 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /RefreshControlDemo/TextRefreshingController.h: -------------------------------------------------------------------------------- 1 | // TextRefreshingController.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface TextRefreshingController : UITableViewController 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /RefreshControlDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // AppDelegate.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface AppDelegate : UIResponder 26 | 27 | @property (strong, nonatomic) UIWindow *window; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /CKRefreshControl/CKRefreshArrowView.h: -------------------------------------------------------------------------------- 1 | // CKRefreshArrowView.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface CKRefreshArrowView : UIView 26 | 27 | @property (nonatomic) CGFloat progress; 28 | @property (nonatomic) UIColor *tintColor; // default = 0.5 gray, 1.0 alpha 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /RefreshControlDemo/main.m: -------------------------------------------------------------------------------- 1 | // main.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | #import "AppDelegate.h" 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | @autoreleasepool { 30 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /RefreshControlDemo/RefreshControlDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.instructure.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /CKRefreshControl/CKParagraphStyle.h: -------------------------------------------------------------------------------- 1 | // CKParagraphStyle.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface CKParagraphStyle : NSObject 26 | 27 | @end 28 | 29 | 30 | @class NSAttributedString; 31 | @class NSParagraphStyle; 32 | @interface NSAttributedString (CKParagraphStyleAdditions) 33 | // This will be added to the class at runtime if not already available 34 | @property (nonatomic,retain) NSParagraphStyle *paragraphStyle; 35 | @end 36 | -------------------------------------------------------------------------------- /CKRefreshControl/CKParagraphStyle.m: -------------------------------------------------------------------------------- 1 | // CKParagraphStyle.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "CKParagraphStyle.h" 24 | #import 25 | 26 | @implementation CKParagraphStyle 27 | 28 | #if __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_5_1 29 | #define IMP_WITH_BLOCK_TYPE __bridge void* 30 | #else 31 | #define IMP_WITH_BLOCK_TYPE id 32 | #endif 33 | 34 | - (id) initWithCoder: (NSCoder *) aDecoder 35 | { 36 | return [super init]; 37 | } 38 | 39 | + (void) load 40 | { 41 | if (objc_getClass("NSParagraphStyle")) 42 | return; 43 | 44 | // CKParagraphStyle will masquerade as NSParagraphStyle 45 | static dispatch_once_t registerNSParagraphStyleClass_onceToken; 46 | dispatch_once(®isterNSParagraphStyleClass_onceToken, ^{ 47 | Class nsParagraphStyleClass = NSClassFromString(@"NSParagraphStyle"); 48 | if (!nsParagraphStyleClass) 49 | { 50 | nsParagraphStyleClass = objc_allocateClassPair(self, "NSParagraphStyle", 0); 51 | objc_registerClassPair(nsParagraphStyleClass); 52 | } 53 | }); 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CKRefreshControl 2 | 3 | Open source library providing iOS 5.0+ support for [UIRefreshControl](http://developer.apple.com/library/ios/#documentation/uikit/reference/UIRefreshControl_class/Reference/Reference.html), which was introduced in iOS 6.0. 4 | 5 | Using it is as simple as this: 6 | 7 | UITableViewController *controller; 8 | 9 | controller.refreshControl = [[UIRefreshControl alloc] init]; 10 | [controller.refreshControl addTarget:self action:@selector(doRefresh:) forControlEvents:UIControlEventValueChanged]; 11 | 12 | Yes, that's exactly the code you'd write on iOS 6. No changes required. 13 | 14 | You can also configure the refresh control in a storyboard, or use the `+appearance` API proxies if you'd like. It all works just like you'd hope, but now you can deploy it back to iOS 5.0 as well. Just link against the static library the `CKRefreshControl` project provides, add the `-ObjC` linker flag, and you're ready to go. 15 | 16 | ![iOS 5 example](https://raw.github.com/instructure/CKRefreshControl/readme-resources/iOS5.png)   17 | ![iOS 6 example](https://raw.github.com/instructure/CKRefreshControl/readme-resources/iOS6.png) 18 | 19 | ---- 20 | 21 | ### Appearance 22 | 23 | CKRefreshControl intentionally does not mimic the iOS 6 UIRefreshControl look and feel for iOS 5.0. Instead, it was designed to look more like the other pull-to-refresh controls commonly used in iOS 5-compatible apps. Thus, whether the user is running iOS 5 or iOS 6, they get an interface that fits in with other apps on the device. 24 | 25 | --- 26 | 27 | ### Why the name? 28 | 29 | CKRefreshControl was originally part of our internal "CanvasKit" library, used in building Instructure's iOS apps. 30 | 31 | --- 32 | 33 | ### Implementation 34 | 35 | In general, you won't even know you're using CKRefreshControl. On iOS 5, we register `UIRefreshControl` as a subclass of `CKRefreshControl`, which implements all the compatibility stuff. On iOS 6, we just get out of the way; `UIRefreshControl` is available natively, and we don't have to do anything. 36 | 37 | --- 38 | 39 | ### But does it work? 40 | 41 | Yes! Instructure is using it in shipping code with no problems. That said, if you find a bug, please let us know and we'll make it work. 42 | 43 | --- 44 | 45 | ### Contributors 46 | 47 | * [@bjhomer](http://github.com/bjhomer) 48 | * [@johnhaitas](http://github.com/johnhaitas) 49 | * [@steipete](http://github.com/steipete) 50 | * [@0xced](http://github.com/0xced) 51 | 52 | --- 53 | 54 | ### License 55 | 56 | CKRefreshControl, and all the accompanying source code, is released under the MIT license. You can see the full text of the license in the accompanying LICENSE.txt file. 57 | -------------------------------------------------------------------------------- /CKRefreshControl/CKRefreshControl.h: -------------------------------------------------------------------------------- 1 | // CKRefreshControl.h 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | /* This is an API-compatible replacement of UIRefreshControl 26 | * It is intended to be an API-compatible replacement 27 | * for platforms where UIRefreshControl is not available. 28 | */ 29 | 30 | @interface CKRefreshControl : UIControl 31 | 32 | 33 | /* The designated initializer 34 | * This initializes a CKRefreshControl with a default height and width. 35 | * Once assigned to a UITableViewController, the frame of the control is managed automatically. 36 | * When a user has pulled-to-refresh, the CKRefreshControl fires its UIControlEventValueChanged event. 37 | */ 38 | - (id)init; 39 | 40 | @property (nonatomic, readonly, getter=isRefreshing) BOOL refreshing; 41 | 42 | // This 'Tint Color' will set the text color and spinner color 43 | @property (nonatomic, retain) UIColor *tintColor UI_APPEARANCE_SELECTOR; // Default = 0.5 gray, 1.0 alpha 44 | @property (nonatomic, retain) NSAttributedString *attributedTitle UI_APPEARANCE_SELECTOR; 45 | 46 | // May be used to indicate to the refreshControl that an external event has initiated the refresh action 47 | - (void)beginRefreshing; 48 | // Must be explicitly called when the refreshing has completed 49 | - (void)endRefreshing; 50 | 51 | 52 | @end 53 | 54 | 55 | @class UIRefreshControl; 56 | @interface UITableViewController (CKRefreshControlAdditions) 57 | // This will be added to the class at runtime if not already available 58 | @property (nonatomic,retain) UIRefreshControl *refreshControl; 59 | @end 60 | -------------------------------------------------------------------------------- /RefreshControlDemo/AppearanceCustomizationController.m: -------------------------------------------------------------------------------- 1 | // AppearanceCustomizationController.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "AppearanceCustomizationController.h" 24 | #import 25 | 26 | @implementation AppearanceCustomizationController 27 | 28 | - (void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | 32 | self.refreshControl = [[UIRefreshControl alloc] init]; 33 | [self.refreshControl addTarget:self action:@selector(doRefresh:) forControlEvents:UIControlEventValueChanged]; 34 | } 35 | 36 | - (void)doRefresh:(CKRefreshControl *)sender { 37 | NSLog(@"refreshing"); 38 | [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1.0]; 39 | } 40 | 41 | #pragma mark - Table view data source 42 | 43 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 44 | { 45 | // Return the number of sections. 46 | return 1; 47 | } 48 | 49 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 50 | { 51 | // Return the number of rows in the section. 52 | return 1; 53 | } 54 | 55 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 56 | { 57 | static NSString *CellIdentifier = @"Cell"; 58 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 59 | 60 | return cell; 61 | } 62 | 63 | #pragma mark - Table view delegate 64 | 65 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 66 | { 67 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /RefreshControlDemo/DefaultRefreshController.m: -------------------------------------------------------------------------------- 1 | // DefaultRefreshController.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "DefaultRefreshController.h" 24 | #import 25 | 26 | @implementation DefaultRefreshController 27 | 28 | - (void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | 32 | self.refreshControl = [[UIRefreshControl alloc] init]; 33 | [self.refreshControl addTarget:self action:@selector(doRefresh:) forControlEvents:UIControlEventValueChanged]; 34 | } 35 | 36 | - (void)doRefresh:(CKRefreshControl *)sender { 37 | NSLog(@"refreshing"); 38 | [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1.0]; 39 | } 40 | 41 | #pragma mark - Table view data source 42 | 43 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 44 | { 45 | // Return the number of sections. 46 | return 1; 47 | } 48 | 49 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 50 | { 51 | // Return the number of rows in the section. 52 | return 20; 53 | } 54 | 55 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 56 | { 57 | static NSString *CellIdentifier = @"Cell"; 58 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 59 | 60 | // Configure the cell... 61 | cell.textLabel.text = @"Pull down!"; 62 | 63 | return cell; 64 | } 65 | 66 | #pragma mark - Table view delegate 67 | 68 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 69 | { 70 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /RefreshControlDemo/TextRefreshingController.m: -------------------------------------------------------------------------------- 1 | // TextRefreshingController.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "TextRefreshingController.h" 24 | #import 25 | 26 | @interface TextRefreshingController () 27 | 28 | @end 29 | 30 | @implementation TextRefreshingController 31 | 32 | - (void)viewDidLoad 33 | { 34 | [super viewDidLoad]; 35 | 36 | self.refreshControl = [[UIRefreshControl alloc] init]; 37 | self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"This is a test"]; 38 | [self.refreshControl addTarget:self action:@selector(doRefresh:) forControlEvents:UIControlEventValueChanged]; 39 | } 40 | 41 | - (void)doRefresh:(CKRefreshControl *)sender { 42 | NSLog(@"refreshing"); 43 | [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1.0]; 44 | } 45 | 46 | #pragma mark - Table view data source 47 | 48 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 49 | { 50 | // Return the number of sections. 51 | return 1; 52 | } 53 | 54 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 55 | { 56 | // Return the number of rows in the section. 57 | return 1; 58 | } 59 | 60 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 61 | { 62 | static NSString *CellIdentifier = @"Cell"; 63 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 64 | 65 | // Configure the cell... 66 | cell.textLabel.text = @"With text!"; 67 | 68 | return cell; 69 | } 70 | 71 | #pragma mark - Table view delegate 72 | 73 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 74 | { 75 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # .gitignore file for Xcode4 / OS X Source projects 3 | # 4 | # NB: if you are storing "built" products, this WILL NOT WORK, 5 | # and you should use a different .gitignore (or none at all) 6 | # This file is for SOURCE projects, where there are many extra 7 | # files that we want to exclude 8 | # 9 | # For updates, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 10 | ######################### 11 | 12 | ##### 13 | # OS X temporary files that should never be committed 14 | 15 | .DS_Store 16 | *.swp 17 | *.lock 18 | profile 19 | 20 | 21 | #### 22 | # Xcode temporary files that should never be committed 23 | # 24 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 25 | 26 | *~.nib 27 | 28 | 29 | #### 30 | # Xcode build files - 31 | # 32 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 33 | 34 | DerivedData/ 35 | 36 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 37 | 38 | build/ 39 | 40 | 41 | ##### 42 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 43 | # 44 | # This is complicated: 45 | # 46 | # SOMETIMES you need to put this file in version control. 47 | # Apple designed it poorly - if you use "custom executables", they are 48 | # saved in this file. 49 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 50 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 51 | 52 | *.pbxuser 53 | *.mode1v3 54 | *.mode2v3 55 | *.perspectivev3 56 | # NB: also, whitelist the default ones, some projects need to use these 57 | !default.pbxuser 58 | !default.mode1v3 59 | !default.mode2v3 60 | !default.perspectivev3 61 | 62 | 63 | #### 64 | # Xcode 4 - semi-personal settings, often included in workspaces 65 | # 66 | # You can safely ignore the xcuserdata files - but do NOT ignore the files next to them 67 | # 68 | 69 | xcuserdata 70 | 71 | 72 | #### 73 | # XCode 4 workspaces - more detailed 74 | # 75 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 76 | # 77 | # Workspace layout is quite spammy. For reference: 78 | # 79 | # (root)/ 80 | # (project-name).xcodeproj/ 81 | # project.pbxproj 82 | # project.xcworkspace/ 83 | # contents.xcworkspacedata 84 | # xcuserdata/ 85 | # (your name)/xcuserdatad/ 86 | # xcuserdata/ 87 | # (your name)/xcuserdatad/ 88 | # 89 | # 90 | # 91 | # Xcode 4 workspaces - SHARED 92 | # 93 | # This is UNDOCUMENTED (google: "developer.apple.com xcshareddata" - 0 results 94 | # But if you're going to kill personal workspaces, at least keep the shared ones... 95 | # 96 | # 97 | *.xcworkspace 98 | !xcworkspacedata 99 | 100 | 101 | #### 102 | # Xcode 4 - Deprecated classes 103 | # 104 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 105 | # 106 | # We're using source-control, so this is a "feature" that we do not want! 107 | 108 | *.moved-aside 109 | 110 | 111 | #### 112 | # UNKNOWN: recommended by others, but I can't discover what these files are 113 | # 114 | # ...none. Everything is now explained. 115 | 116 | -------------------------------------------------------------------------------- /RefreshControlDemo/ColorRefreshController.m: -------------------------------------------------------------------------------- 1 | // ColorRefreshController.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "ColorRefreshController.h" 24 | #import 25 | 26 | @implementation ColorRefreshController { 27 | NSDictionary *colors; 28 | } 29 | 30 | - (void)viewDidLoad 31 | { 32 | [super viewDidLoad]; 33 | 34 | colors = @{ 35 | @"Orange" : [UIColor orangeColor], 36 | @"Red" : [UIColor redColor], 37 | @"Blue" : [UIColor blueColor], 38 | @"Gray" : [UIColor grayColor], 39 | @"Black" : [UIColor blackColor], 40 | }; 41 | 42 | self.refreshControl = [[UIRefreshControl alloc] init]; 43 | self.refreshControl.tintColor = [UIColor orangeColor]; 44 | [self.refreshControl addTarget:self action:@selector(doRefresh:) forControlEvents:UIControlEventValueChanged]; 45 | } 46 | 47 | - (void)doRefresh:(CKRefreshControl *)sender { 48 | NSLog(@"refreshing"); 49 | [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1.0]; 50 | } 51 | 52 | #pragma mark - Table view data source 53 | 54 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 55 | { 56 | // Return the number of sections. 57 | return 1; 58 | } 59 | 60 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 61 | { 62 | // Return the number of rows in the section. 63 | return colors.count; 64 | } 65 | 66 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 67 | { 68 | static NSString *CellIdentifier = @"Cell"; 69 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 70 | 71 | // Configure the cell... 72 | cell.textLabel.text = [colors allKeys][indexPath.row]; 73 | 74 | return cell; 75 | } 76 | 77 | #pragma mark - Table view delegate 78 | 79 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 80 | { 81 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 82 | NSArray *colorNames = [colors allKeys]; 83 | NSString *selectedName = colorNames[indexPath.row]; 84 | self.refreshControl.tintColor = colors[selectedName]; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /RefreshControlDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // AppDelegate.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "AppDelegate.h" 24 | #import "AppearanceCustomizationController.h" 25 | #import 26 | 27 | @implementation AppDelegate 28 | 29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 30 | { 31 | application.statusBarStyle = UIStatusBarStyleBlackOpaque; 32 | 33 | [[UIRefreshControl appearanceWhenContainedIn:[AppearanceCustomizationController class], nil] setTintColor:[UIColor greenColor]]; 34 | 35 | // Override point for customization after application launch. 36 | return YES; 37 | } 38 | 39 | - (void)applicationWillResignActive:(UIApplication *)application 40 | { 41 | // 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. 42 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 43 | } 44 | 45 | - (void)applicationDidEnterBackground:(UIApplication *)application 46 | { 47 | // 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. 48 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 49 | } 50 | 51 | - (void)applicationWillEnterForeground:(UIApplication *)application 52 | { 53 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 54 | } 55 | 56 | - (void)applicationDidBecomeActive:(UIApplication *)application 57 | { 58 | // 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. 59 | } 60 | 61 | - (void)applicationWillTerminate:(UIApplication *)application 62 | { 63 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /RefreshControlDemo/StoryboardViewController.m: -------------------------------------------------------------------------------- 1 | // StoryboardViewController.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "StoryboardViewController.h" 24 | #import 25 | 26 | @interface StoryboardViewController () 27 | 28 | @end 29 | 30 | @implementation StoryboardViewController 31 | 32 | - (id)initWithStyle:(UITableViewStyle)style 33 | { 34 | self = [super initWithStyle:style]; 35 | if (self) { 36 | // Custom initialization 37 | } 38 | return self; 39 | } 40 | 41 | - (void)viewDidLoad 42 | { 43 | [super viewDidLoad]; 44 | 45 | // Uncomment the following line to preserve selection between presentations. 46 | // self.clearsSelectionOnViewWillAppear = NO; 47 | 48 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 49 | // self.navigationItem.rightBarButtonItem = self.editButtonItem; 50 | 51 | [self.refreshControl addTarget:self action:@selector(doRefresh:) forControlEvents:UIControlEventValueChanged]; 52 | } 53 | 54 | - (void)doRefresh:(CKRefreshControl *)sender { 55 | NSLog(@"refreshing"); 56 | [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1.0]; 57 | } 58 | 59 | - (void)didReceiveMemoryWarning 60 | { 61 | [super didReceiveMemoryWarning]; 62 | // Dispose of any resources that can be recreated. 63 | } 64 | 65 | #pragma mark - Table view data source 66 | 67 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 68 | { 69 | // Return the number of sections. 70 | return 1; 71 | } 72 | 73 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 74 | { 75 | // Return the number of rows in the section. 76 | return 1; 77 | } 78 | 79 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 80 | { 81 | static NSString *CellIdentifier = @"Cell"; 82 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 83 | 84 | // Configure the cell... 85 | cell.textLabel.text = @"loaded from storyboard"; 86 | 87 | return cell; 88 | } 89 | 90 | #pragma mark - Table view delegate 91 | 92 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 93 | { 94 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /CKRefreshControl/CKRefreshArrowView.m: -------------------------------------------------------------------------------- 1 | // CKRefreshArrowView.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "CKRefreshArrowView.h" 24 | #import 25 | 26 | #if !__has_feature(objc_arc) 27 | #error Add -fobjc-arc to the compile flags for CKRefreshArrowView.m 28 | #endif 29 | 30 | @implementation CKRefreshArrowView { 31 | CGFloat range; 32 | CGFloat arrowRadius; 33 | CGFloat lineWidth; 34 | 35 | UIColor *glowColor; 36 | } 37 | 38 | + (Class)layerClass { 39 | return [CAShapeLayer class]; 40 | } 41 | 42 | static void commonSetup(CKRefreshArrowView *self) { 43 | self.tintColor = [UIColor colorWithWhite:0.5 alpha:1]; 44 | self.backgroundColor = [UIColor clearColor]; 45 | 46 | self->range = (M_PI * 1.5); 47 | self.layer.shadowOpacity = 0; 48 | self.layer.shadowRadius = 2; 49 | self.layer.shadowOffset = (CGSizeZero); 50 | } 51 | 52 | - (id)initWithFrame:(CGRect)frame { 53 | self = [super initWithFrame:frame]; 54 | 55 | if (self) { 56 | commonSetup(self); 57 | } 58 | return self; 59 | } 60 | 61 | - (void)awakeFromNib { 62 | [super awakeFromNib]; 63 | commonSetup(self); 64 | } 65 | 66 | 67 | - (void)setTintColor:(UIColor *)tintColor { 68 | if (tintColor == nil) { 69 | tintColor = [UIColor colorWithWhite:0.5 alpha:1.0]; 70 | } 71 | _tintColor = tintColor; 72 | [self calculateGlowColor]; 73 | [self setNeedsDisplay]; 74 | } 75 | 76 | - (void)layoutSubviews { 77 | [super layoutSubviews]; 78 | 79 | CGFloat minDimension = MIN(self.bounds.size.height, self.bounds.size.width); 80 | 81 | arrowRadius = roundf(minDimension * 0.32); 82 | lineWidth = roundf(arrowRadius * 0.45); 83 | self.layer.shadowRadius = lineWidth * 0.2; 84 | } 85 | 86 | - (void)setProgress:(CGFloat)progress { 87 | _progress = progress; 88 | [self setNeedsDisplay]; 89 | } 90 | 91 | - (CAShapeLayer *)shapeLayer { 92 | return (CAShapeLayer *)self.layer; 93 | } 94 | 95 | - (void)drawRect:(CGRect)rect { 96 | [self updateShapeLayerPath]; 97 | [super drawRect:rect]; 98 | } 99 | 100 | - (void)calculateGlowColor { 101 | CGFloat hue = 0, saturation = 0, brightness = 0, alpha = 0; 102 | CGColorSpaceRef colorSpace = CGColorGetColorSpace([self.tintColor CGColor]); 103 | if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelMonochrome) { 104 | [self.tintColor getWhite:&brightness alpha:&alpha]; 105 | } 106 | else { 107 | [self.tintColor getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]; 108 | } 109 | 110 | // If it's in the middle, pull toward the extreme. If it's at an extreme, pull toward the middle. 111 | CGFloat centerThreshold = 0.5; 112 | CGFloat edgeThreshold = 0.15; 113 | CGFloat glowFactor = 0.35; 114 | 115 | brightness *= alpha; 116 | CGFloat glowBrightness = 0.0; 117 | 118 | if ((brightness > edgeThreshold && brightness <= centerThreshold) || brightness > (1 - edgeThreshold)) { 119 | // Going darker 120 | glowBrightness = brightness - glowFactor; 121 | } 122 | else { 123 | // Going lighter 124 | glowBrightness = brightness + glowFactor; 125 | } 126 | glowColor = [UIColor colorWithHue:hue saturation:saturation brightness:glowBrightness alpha:1]; 127 | self.layer.shadowColor = [[UIColor colorWithHue:0 saturation:0 brightness:glowBrightness - 0.3 alpha:0.8] CGColor]; 128 | } 129 | 130 | - (void)updateShapeLayerPath { 131 | 132 | CGFloat effectiveProgress = MIN(self.progress, 1.0); 133 | BOOL shouldGlow = (self.progress >= 1.0); 134 | 135 | CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 136 | 137 | CGFloat endAngle = (effectiveProgress * range); 138 | CGFloat outerRadius = arrowRadius + lineWidth / 2; 139 | CGFloat innerRadius = arrowRadius - lineWidth / 2; 140 | 141 | 142 | // Start constructing the path 143 | UIBezierPath *path = [UIBezierPath bezierPath]; 144 | 145 | CGPoint start = [self pointAtProgress:0 radius:arrowRadius center:center]; 146 | [path moveToPoint:start]; 147 | 148 | // Arc to the arrow 149 | [path addArcWithCenter:center 150 | radius:outerRadius 151 | startAngle:0 152 | endAngle:endAngle 153 | clockwise:YES]; 154 | 155 | // Build the arrow head 156 | CGPoint arrowStart = [self pointAtProgress:effectiveProgress radius:outerRadius + lineWidth center:center]; 157 | 158 | CGPoint tangent = (CGPoint){.x = cosf(endAngle + M_PI_2), .y = sinf(endAngle + M_PI_2)}; 159 | CGPoint arrowTip = [self pointAtProgress:effectiveProgress radius:arrowRadius center:center]; 160 | arrowTip.x += tangent.x * lineWidth * 2; 161 | arrowTip.y += tangent.y * lineWidth * 2; 162 | 163 | CGPoint arrowEnd = [self pointAtProgress:effectiveProgress radius:innerRadius - lineWidth center:center]; 164 | 165 | [path addLineToPoint:arrowStart]; 166 | [path addLineToPoint:arrowTip]; 167 | [path addLineToPoint:arrowEnd]; 168 | 169 | // Arc back to the start 170 | CGPoint innerEnd = [self pointAtProgress:effectiveProgress radius:innerRadius center:center]; 171 | [path addLineToPoint:innerEnd]; 172 | 173 | [path addArcWithCenter:center 174 | radius:innerRadius 175 | startAngle:endAngle 176 | endAngle:0 177 | clockwise:NO]; 178 | 179 | // Wrap it all up 180 | [path addLineToPoint:start]; 181 | self.shapeLayer.path = [path CGPath]; 182 | 183 | 184 | // Animate on/off the glow if we've passed the threshold 185 | [CATransaction begin]; 186 | [CATransaction setAnimationDuration:0.4]; 187 | self.layer.shadowOpacity = (shouldGlow ? 1.0 : 0.0); 188 | self.layer.shadowPath = [path CGPath]; 189 | 190 | CGColorRef targetColor = nil; 191 | if (shouldGlow) { 192 | targetColor = [glowColor CGColor]; 193 | } 194 | else { 195 | targetColor = [self.tintColor CGColor]; 196 | } 197 | 198 | 199 | if (CGColorEqualToColor(targetColor, self.shapeLayer.fillColor) == NO) { 200 | self.shapeLayer.fillColor = targetColor; 201 | } 202 | [CATransaction commit]; 203 | 204 | } 205 | 206 | - (CGPoint)pointAtProgress:(CGFloat)progress radius:(CGFloat)radius center:(CGPoint)center { 207 | float x = cosf(range * progress) * radius; 208 | float y = sinf(range * progress) * radius; 209 | 210 | x += center.x; 211 | y += center.y; 212 | 213 | return (CGPoint){x, y}; 214 | } 215 | 216 | - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { 217 | // Enable implicit animations for shadow* and fillColor properties 218 | if ([event hasPrefix:@"shadow"] || [event hasPrefix:@"fillColor"]) { 219 | return nil; 220 | } 221 | return [super actionForLayer:layer forKey:event]; 222 | } 223 | 224 | 225 | 226 | @end 227 | -------------------------------------------------------------------------------- /CKRefreshControl.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 589E4E5C16AB267E00E9E0DF /* CKParagraphStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 589E4E5B16AB267E00E9E0DF /* CKParagraphStyle.m */; }; 11 | B71610F61631C4EB00B08595 /* CKRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = B71610F51631C4EB00B08595 /* CKRefreshControl.m */; }; 12 | B71610F71631C4F400B08595 /* CKRefreshControl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B71610F41631C4EB00B08595 /* CKRefreshControl.h */; }; 13 | B71610FA1631C54E00B08595 /* CKRefreshArrowView.m in Sources */ = {isa = PBXBuildFile; fileRef = B71610F91631C54E00B08595 /* CKRefreshArrowView.m */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | B71610E11631C4BC00B08595 /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = "include/${PRODUCT_NAME}"; 21 | dstSubfolderSpec = 16; 22 | files = ( 23 | B71610F71631C4F400B08595 /* CKRefreshControl.h in CopyFiles */, 24 | ); 25 | runOnlyForDeploymentPostprocessing = 0; 26 | }; 27 | /* End PBXCopyFilesBuildPhase section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 589E4E5A16AB267E00E9E0DF /* CKParagraphStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKParagraphStyle.h; sourceTree = ""; }; 31 | 589E4E5B16AB267E00E9E0DF /* CKParagraphStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKParagraphStyle.m; sourceTree = ""; }; 32 | B71610E31631C4BC00B08595 /* libCKRefreshControl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCKRefreshControl.a; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | B71610EA1631C4BC00B08595 /* CKRefreshControl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CKRefreshControl-Prefix.pch"; sourceTree = ""; }; 34 | B71610F41631C4EB00B08595 /* CKRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKRefreshControl.h; sourceTree = ""; }; 35 | B71610F51631C4EB00B08595 /* CKRefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKRefreshControl.m; sourceTree = ""; }; 36 | B71610F81631C54E00B08595 /* CKRefreshArrowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKRefreshArrowView.h; sourceTree = ""; }; 37 | B71610F91631C54E00B08595 /* CKRefreshArrowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKRefreshArrowView.m; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | B71610E01631C4BC00B08595 /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | B71610D81631C4BC00B08595 = { 52 | isa = PBXGroup; 53 | children = ( 54 | B71610E81631C4BC00B08595 /* CKRefreshControl */, 55 | B71610E41631C4BC00B08595 /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | B71610E41631C4BC00B08595 /* Products */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | B71610E31631C4BC00B08595 /* libCKRefreshControl.a */, 63 | ); 64 | name = Products; 65 | sourceTree = ""; 66 | }; 67 | B71610E81631C4BC00B08595 /* CKRefreshControl */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | B71610E91631C4BC00B08595 /* Supporting Files */, 71 | B71610F41631C4EB00B08595 /* CKRefreshControl.h */, 72 | B71610F51631C4EB00B08595 /* CKRefreshControl.m */, 73 | B71610F81631C54E00B08595 /* CKRefreshArrowView.h */, 74 | B71610F91631C54E00B08595 /* CKRefreshArrowView.m */, 75 | 589E4E5A16AB267E00E9E0DF /* CKParagraphStyle.h */, 76 | 589E4E5B16AB267E00E9E0DF /* CKParagraphStyle.m */, 77 | ); 78 | path = CKRefreshControl; 79 | sourceTree = ""; 80 | }; 81 | B71610E91631C4BC00B08595 /* Supporting Files */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | B71610EA1631C4BC00B08595 /* CKRefreshControl-Prefix.pch */, 85 | ); 86 | name = "Supporting Files"; 87 | sourceTree = ""; 88 | }; 89 | /* End PBXGroup section */ 90 | 91 | /* Begin PBXNativeTarget section */ 92 | B71610E21631C4BC00B08595 /* CKRefreshControl */ = { 93 | isa = PBXNativeTarget; 94 | buildConfigurationList = B71610F11631C4BC00B08595 /* Build configuration list for PBXNativeTarget "CKRefreshControl" */; 95 | buildPhases = ( 96 | B71610DF1631C4BC00B08595 /* Sources */, 97 | B71610E01631C4BC00B08595 /* Frameworks */, 98 | B71610E11631C4BC00B08595 /* CopyFiles */, 99 | ); 100 | buildRules = ( 101 | ); 102 | dependencies = ( 103 | ); 104 | name = CKRefreshControl; 105 | productName = CKRefreshControl; 106 | productReference = B71610E31631C4BC00B08595 /* libCKRefreshControl.a */; 107 | productType = "com.apple.product-type.library.static"; 108 | }; 109 | /* End PBXNativeTarget section */ 110 | 111 | /* Begin PBXProject section */ 112 | B71610DA1631C4BC00B08595 /* Project object */ = { 113 | isa = PBXProject; 114 | attributes = { 115 | LastUpgradeCheck = 0450; 116 | ORGANIZATIONNAME = Instructure; 117 | }; 118 | buildConfigurationList = B71610DD1631C4BC00B08595 /* Build configuration list for PBXProject "CKRefreshControl" */; 119 | compatibilityVersion = "Xcode 3.2"; 120 | developmentRegion = English; 121 | hasScannedForEncodings = 0; 122 | knownRegions = ( 123 | en, 124 | ); 125 | mainGroup = B71610D81631C4BC00B08595; 126 | productRefGroup = B71610E41631C4BC00B08595 /* Products */; 127 | projectDirPath = ""; 128 | projectRoot = ""; 129 | targets = ( 130 | B71610E21631C4BC00B08595 /* CKRefreshControl */, 131 | ); 132 | }; 133 | /* End PBXProject section */ 134 | 135 | /* Begin PBXSourcesBuildPhase section */ 136 | B71610DF1631C4BC00B08595 /* Sources */ = { 137 | isa = PBXSourcesBuildPhase; 138 | buildActionMask = 2147483647; 139 | files = ( 140 | B71610F61631C4EB00B08595 /* CKRefreshControl.m in Sources */, 141 | B71610FA1631C54E00B08595 /* CKRefreshArrowView.m in Sources */, 142 | 589E4E5C16AB267E00E9E0DF /* CKParagraphStyle.m in Sources */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXSourcesBuildPhase section */ 147 | 148 | /* Begin XCBuildConfiguration section */ 149 | B71610EF1631C4BC00B08595 /* Debug */ = { 150 | isa = XCBuildConfiguration; 151 | buildSettings = { 152 | ALWAYS_SEARCH_USER_PATHS = NO; 153 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 154 | CLANG_CXX_LIBRARY = "libc++"; 155 | CLANG_ENABLE_OBJC_ARC = YES; 156 | CLANG_WARN_EMPTY_BODY = YES; 157 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 158 | COPY_PHASE_STRIP = NO; 159 | GCC_C_LANGUAGE_STANDARD = gnu99; 160 | GCC_DYNAMIC_NO_PIC = NO; 161 | GCC_OPTIMIZATION_LEVEL = 0; 162 | GCC_PREPROCESSOR_DEFINITIONS = ( 163 | "DEBUG=1", 164 | "$(inherited)", 165 | ); 166 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 167 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 168 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 169 | GCC_WARN_UNUSED_VARIABLE = YES; 170 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 171 | ONLY_ACTIVE_ARCH = YES; 172 | SDKROOT = iphoneos; 173 | }; 174 | name = Debug; 175 | }; 176 | B71610F01631C4BC00B08595 /* Release */ = { 177 | isa = XCBuildConfiguration; 178 | buildSettings = { 179 | ALWAYS_SEARCH_USER_PATHS = NO; 180 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 181 | CLANG_CXX_LIBRARY = "libc++"; 182 | CLANG_ENABLE_OBJC_ARC = YES; 183 | CLANG_WARN_EMPTY_BODY = YES; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | COPY_PHASE_STRIP = YES; 186 | GCC_C_LANGUAGE_STANDARD = gnu99; 187 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 188 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 189 | GCC_WARN_UNUSED_VARIABLE = YES; 190 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 191 | SDKROOT = iphoneos; 192 | VALIDATE_PRODUCT = YES; 193 | }; 194 | name = Release; 195 | }; 196 | B71610F21631C4BC00B08595 /* Debug */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | DSTROOT = /tmp/CKRefreshControl.dst; 200 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 201 | GCC_PREFIX_HEADER = "CKRefreshControl/CKRefreshControl-Prefix.pch"; 202 | OTHER_LDFLAGS = "-ObjC"; 203 | PRODUCT_NAME = "$(TARGET_NAME)"; 204 | SKIP_INSTALL = YES; 205 | }; 206 | name = Debug; 207 | }; 208 | B71610F31631C4BC00B08595 /* Release */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | DSTROOT = /tmp/CKRefreshControl.dst; 212 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 213 | GCC_PREFIX_HEADER = "CKRefreshControl/CKRefreshControl-Prefix.pch"; 214 | OTHER_LDFLAGS = "-ObjC"; 215 | PRODUCT_NAME = "$(TARGET_NAME)"; 216 | SKIP_INSTALL = YES; 217 | }; 218 | name = Release; 219 | }; 220 | /* End XCBuildConfiguration section */ 221 | 222 | /* Begin XCConfigurationList section */ 223 | B71610DD1631C4BC00B08595 /* Build configuration list for PBXProject "CKRefreshControl" */ = { 224 | isa = XCConfigurationList; 225 | buildConfigurations = ( 226 | B71610EF1631C4BC00B08595 /* Debug */, 227 | B71610F01631C4BC00B08595 /* Release */, 228 | ); 229 | defaultConfigurationIsVisible = 0; 230 | defaultConfigurationName = Release; 231 | }; 232 | B71610F11631C4BC00B08595 /* Build configuration list for PBXNativeTarget "CKRefreshControl" */ = { 233 | isa = XCConfigurationList; 234 | buildConfigurations = ( 235 | B71610F21631C4BC00B08595 /* Debug */, 236 | B71610F31631C4BC00B08595 /* Release */, 237 | ); 238 | defaultConfigurationIsVisible = 0; 239 | defaultConfigurationName = Release; 240 | }; 241 | /* End XCConfigurationList section */ 242 | }; 243 | rootObject = B71610DA1631C4BC00B08595 /* Project object */; 244 | } 245 | -------------------------------------------------------------------------------- /RefreshControlDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 58E229DE16A9D76000177BFF /* StoryboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58E229DD16A9D76000177BFF /* StoryboardViewController.m */; }; 11 | B70037231631D51700FA173A /* ColorRefreshController.m in Sources */ = {isa = PBXBuildFile; fileRef = B700371E1631D51700FA173A /* ColorRefreshController.m */; }; 12 | B70037241631D51700FA173A /* DefaultRefreshController.m in Sources */ = {isa = PBXBuildFile; fileRef = B70037201631D51700FA173A /* DefaultRefreshController.m */; }; 13 | B70037251631D51700FA173A /* TextRefreshingController.m in Sources */ = {isa = PBXBuildFile; fileRef = B70037221631D51700FA173A /* TextRefreshingController.m */; }; 14 | B70037291631D51B00FA173A /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B70037271631D51B00FA173A /* MainStoryboard.storyboard */; }; 15 | B70037441631D57200FA173A /* libCKRefreshControl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B70037341631D55200FA173A /* libCKRefreshControl.a */; }; 16 | B70037491631D67200FA173A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B70037481631D67200FA173A /* QuartzCore.framework */; }; 17 | B700374C1635A5BF00FA173A /* AppearanceCustomizationController.m in Sources */ = {isa = PBXBuildFile; fileRef = B700374B1635A5BF00FA173A /* AppearanceCustomizationController.m */; }; 18 | B71611611631D4DA00B08595 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71611601631D4DA00B08595 /* UIKit.framework */; }; 19 | B71611631631D4DA00B08595 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71611621631D4DA00B08595 /* Foundation.framework */; }; 20 | B71611651631D4DA00B08595 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71611641631D4DA00B08595 /* CoreGraphics.framework */; }; 21 | B716116B1631D4DA00B08595 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B71611691631D4DA00B08595 /* InfoPlist.strings */; }; 22 | B716116D1631D4DA00B08595 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B716116C1631D4DA00B08595 /* main.m */; }; 23 | B71611711631D4DA00B08595 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B71611701631D4DA00B08595 /* AppDelegate.m */; }; 24 | B71611731631D4DA00B08595 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = B71611721631D4DA00B08595 /* Default.png */; }; 25 | B71611751631D4DA00B08595 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B71611741631D4DA00B08595 /* Default@2x.png */; }; 26 | B71611771631D4DA00B08595 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B71611761631D4DA00B08595 /* Default-568h@2x.png */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | B70037331631D55200FA173A /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = B700372F1631D55100FA173A /* CKRefreshControl.xcodeproj */; 33 | proxyType = 2; 34 | remoteGlobalIDString = B71610E31631C4BC00B08595; 35 | remoteInfo = CKRefreshControl; 36 | }; 37 | B70037451631D58E00FA173A /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = B700372F1631D55100FA173A /* CKRefreshControl.xcodeproj */; 40 | proxyType = 1; 41 | remoteGlobalIDString = B71610E21631C4BC00B08595; 42 | remoteInfo = CKRefreshControl; 43 | }; 44 | /* End PBXContainerItemProxy section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | 58E229DC16A9D76000177BFF /* StoryboardViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StoryboardViewController.h; sourceTree = ""; }; 48 | 58E229DD16A9D76000177BFF /* StoryboardViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StoryboardViewController.m; sourceTree = ""; }; 49 | B700371D1631D51700FA173A /* ColorRefreshController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorRefreshController.h; sourceTree = ""; }; 50 | B700371E1631D51700FA173A /* ColorRefreshController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ColorRefreshController.m; sourceTree = ""; }; 51 | B700371F1631D51700FA173A /* DefaultRefreshController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultRefreshController.h; sourceTree = ""; }; 52 | B70037201631D51700FA173A /* DefaultRefreshController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DefaultRefreshController.m; sourceTree = ""; }; 53 | B70037211631D51700FA173A /* TextRefreshingController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextRefreshingController.h; sourceTree = ""; }; 54 | B70037221631D51700FA173A /* TextRefreshingController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextRefreshingController.m; sourceTree = ""; }; 55 | B70037281631D51B00FA173A /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 56 | B700372F1631D55100FA173A /* CKRefreshControl.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = CKRefreshControl.xcodeproj; sourceTree = ""; }; 57 | B70037481631D67200FA173A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 58 | B700374A1635A5BF00FA173A /* AppearanceCustomizationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppearanceCustomizationController.h; sourceTree = ""; }; 59 | B700374B1635A5BF00FA173A /* AppearanceCustomizationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppearanceCustomizationController.m; sourceTree = ""; }; 60 | B716115C1631D4DA00B08595 /* RefreshControlDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RefreshControlDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | B71611601631D4DA00B08595 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 62 | B71611621631D4DA00B08595 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 63 | B71611641631D4DA00B08595 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 64 | B71611681631D4DA00B08595 /* RefreshControlDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RefreshControlDemo-Info.plist"; sourceTree = ""; }; 65 | B716116A1631D4DA00B08595 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 66 | B716116C1631D4DA00B08595 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 67 | B716116E1631D4DA00B08595 /* RefreshControlDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RefreshControlDemo-Prefix.pch"; sourceTree = ""; }; 68 | B716116F1631D4DA00B08595 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 69 | B71611701631D4DA00B08595 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 70 | B71611721631D4DA00B08595 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 71 | B71611741631D4DA00B08595 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 72 | B71611761631D4DA00B08595 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | B71611591631D4DA00B08595 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | B70037491631D67200FA173A /* QuartzCore.framework in Frameworks */, 81 | B70037441631D57200FA173A /* libCKRefreshControl.a in Frameworks */, 82 | B71611611631D4DA00B08595 /* UIKit.framework in Frameworks */, 83 | B71611631631D4DA00B08595 /* Foundation.framework in Frameworks */, 84 | B71611651631D4DA00B08595 /* CoreGraphics.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | B70037301631D55100FA173A /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | B70037341631D55200FA173A /* libCKRefreshControl.a */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | B71611511631D4DA00B08595 = { 100 | isa = PBXGroup; 101 | children = ( 102 | B71611661631D4DA00B08595 /* RefreshControlDemo */, 103 | B700372F1631D55100FA173A /* CKRefreshControl.xcodeproj */, 104 | B716115F1631D4DA00B08595 /* Frameworks */, 105 | B716115D1631D4DA00B08595 /* Products */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | B716115D1631D4DA00B08595 /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | B716115C1631D4DA00B08595 /* RefreshControlDemo.app */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | B716115F1631D4DA00B08595 /* Frameworks */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | B70037481631D67200FA173A /* QuartzCore.framework */, 121 | B71611601631D4DA00B08595 /* UIKit.framework */, 122 | B71611621631D4DA00B08595 /* Foundation.framework */, 123 | B71611641631D4DA00B08595 /* CoreGraphics.framework */, 124 | ); 125 | name = Frameworks; 126 | sourceTree = ""; 127 | }; 128 | B71611661631D4DA00B08595 /* RefreshControlDemo */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | B716116F1631D4DA00B08595 /* AppDelegate.h */, 132 | B71611701631D4DA00B08595 /* AppDelegate.m */, 133 | B70037271631D51B00FA173A /* MainStoryboard.storyboard */, 134 | B700371D1631D51700FA173A /* ColorRefreshController.h */, 135 | B700371E1631D51700FA173A /* ColorRefreshController.m */, 136 | B700371F1631D51700FA173A /* DefaultRefreshController.h */, 137 | B70037201631D51700FA173A /* DefaultRefreshController.m */, 138 | B70037211631D51700FA173A /* TextRefreshingController.h */, 139 | B70037221631D51700FA173A /* TextRefreshingController.m */, 140 | B700374A1635A5BF00FA173A /* AppearanceCustomizationController.h */, 141 | B700374B1635A5BF00FA173A /* AppearanceCustomizationController.m */, 142 | 58E229DC16A9D76000177BFF /* StoryboardViewController.h */, 143 | 58E229DD16A9D76000177BFF /* StoryboardViewController.m */, 144 | B71611671631D4DA00B08595 /* Supporting Files */, 145 | ); 146 | path = RefreshControlDemo; 147 | sourceTree = ""; 148 | }; 149 | B71611671631D4DA00B08595 /* Supporting Files */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | B71611681631D4DA00B08595 /* RefreshControlDemo-Info.plist */, 153 | B71611691631D4DA00B08595 /* InfoPlist.strings */, 154 | B716116C1631D4DA00B08595 /* main.m */, 155 | B716116E1631D4DA00B08595 /* RefreshControlDemo-Prefix.pch */, 156 | B71611721631D4DA00B08595 /* Default.png */, 157 | B71611741631D4DA00B08595 /* Default@2x.png */, 158 | B71611761631D4DA00B08595 /* Default-568h@2x.png */, 159 | ); 160 | name = "Supporting Files"; 161 | sourceTree = ""; 162 | }; 163 | /* End PBXGroup section */ 164 | 165 | /* Begin PBXNativeTarget section */ 166 | B716115B1631D4DA00B08595 /* RefreshControlDemo */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = B716117A1631D4DA00B08595 /* Build configuration list for PBXNativeTarget "RefreshControlDemo" */; 169 | buildPhases = ( 170 | B71611581631D4DA00B08595 /* Sources */, 171 | B71611591631D4DA00B08595 /* Frameworks */, 172 | B716115A1631D4DA00B08595 /* Resources */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | B70037461631D58E00FA173A /* PBXTargetDependency */, 178 | ); 179 | name = RefreshControlDemo; 180 | productName = RefreshControlDemo; 181 | productReference = B716115C1631D4DA00B08595 /* RefreshControlDemo.app */; 182 | productType = "com.apple.product-type.application"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | B71611531631D4DA00B08595 /* Project object */ = { 188 | isa = PBXProject; 189 | attributes = { 190 | LastUpgradeCheck = 0450; 191 | ORGANIZATIONNAME = "Instructure, Inc."; 192 | }; 193 | buildConfigurationList = B71611561631D4DA00B08595 /* Build configuration list for PBXProject "RefreshControlDemo" */; 194 | compatibilityVersion = "Xcode 3.2"; 195 | developmentRegion = English; 196 | hasScannedForEncodings = 0; 197 | knownRegions = ( 198 | en, 199 | ); 200 | mainGroup = B71611511631D4DA00B08595; 201 | productRefGroup = B716115D1631D4DA00B08595 /* Products */; 202 | projectDirPath = ""; 203 | projectReferences = ( 204 | { 205 | ProductGroup = B70037301631D55100FA173A /* Products */; 206 | ProjectRef = B700372F1631D55100FA173A /* CKRefreshControl.xcodeproj */; 207 | }, 208 | ); 209 | projectRoot = ""; 210 | targets = ( 211 | B716115B1631D4DA00B08595 /* RefreshControlDemo */, 212 | ); 213 | }; 214 | /* End PBXProject section */ 215 | 216 | /* Begin PBXReferenceProxy section */ 217 | B70037341631D55200FA173A /* libCKRefreshControl.a */ = { 218 | isa = PBXReferenceProxy; 219 | fileType = archive.ar; 220 | path = libCKRefreshControl.a; 221 | remoteRef = B70037331631D55200FA173A /* PBXContainerItemProxy */; 222 | sourceTree = BUILT_PRODUCTS_DIR; 223 | }; 224 | /* End PBXReferenceProxy section */ 225 | 226 | /* Begin PBXResourcesBuildPhase section */ 227 | B716115A1631D4DA00B08595 /* Resources */ = { 228 | isa = PBXResourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | B716116B1631D4DA00B08595 /* InfoPlist.strings in Resources */, 232 | B71611731631D4DA00B08595 /* Default.png in Resources */, 233 | B71611751631D4DA00B08595 /* Default@2x.png in Resources */, 234 | B71611771631D4DA00B08595 /* Default-568h@2x.png in Resources */, 235 | B70037291631D51B00FA173A /* MainStoryboard.storyboard in Resources */, 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXResourcesBuildPhase section */ 240 | 241 | /* Begin PBXSourcesBuildPhase section */ 242 | B71611581631D4DA00B08595 /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | B716116D1631D4DA00B08595 /* main.m in Sources */, 247 | B71611711631D4DA00B08595 /* AppDelegate.m in Sources */, 248 | B70037231631D51700FA173A /* ColorRefreshController.m in Sources */, 249 | B70037241631D51700FA173A /* DefaultRefreshController.m in Sources */, 250 | B70037251631D51700FA173A /* TextRefreshingController.m in Sources */, 251 | B700374C1635A5BF00FA173A /* AppearanceCustomizationController.m in Sources */, 252 | 58E229DE16A9D76000177BFF /* StoryboardViewController.m in Sources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXSourcesBuildPhase section */ 257 | 258 | /* Begin PBXTargetDependency section */ 259 | B70037461631D58E00FA173A /* PBXTargetDependency */ = { 260 | isa = PBXTargetDependency; 261 | name = CKRefreshControl; 262 | targetProxy = B70037451631D58E00FA173A /* PBXContainerItemProxy */; 263 | }; 264 | /* End PBXTargetDependency section */ 265 | 266 | /* Begin PBXVariantGroup section */ 267 | B70037271631D51B00FA173A /* MainStoryboard.storyboard */ = { 268 | isa = PBXVariantGroup; 269 | children = ( 270 | B70037281631D51B00FA173A /* en */, 271 | ); 272 | name = MainStoryboard.storyboard; 273 | sourceTree = ""; 274 | }; 275 | B71611691631D4DA00B08595 /* InfoPlist.strings */ = { 276 | isa = PBXVariantGroup; 277 | children = ( 278 | B716116A1631D4DA00B08595 /* en */, 279 | ); 280 | name = InfoPlist.strings; 281 | sourceTree = ""; 282 | }; 283 | /* End PBXVariantGroup section */ 284 | 285 | /* Begin XCBuildConfiguration section */ 286 | B71611781631D4DA00B08595 /* Debug */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_OBJC_ARC = YES; 293 | CLANG_WARN_EMPTY_BODY = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | GCC_C_LANGUAGE_STANDARD = gnu99; 298 | GCC_DYNAMIC_NO_PIC = NO; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 306 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 309 | ONLY_ACTIVE_ARCH = YES; 310 | SDKROOT = iphoneos; 311 | }; 312 | name = Debug; 313 | }; 314 | B71611791631D4DA00B08595 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 319 | CLANG_CXX_LIBRARY = "libc++"; 320 | CLANG_ENABLE_OBJC_ARC = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = YES; 325 | GCC_C_LANGUAGE_STANDARD = gnu99; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 327 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 328 | GCC_WARN_UNUSED_VARIABLE = YES; 329 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 330 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 331 | SDKROOT = iphoneos; 332 | VALIDATE_PRODUCT = YES; 333 | }; 334 | name = Release; 335 | }; 336 | B716117B1631D4DA00B08595 /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 340 | GCC_PREFIX_HEADER = "RefreshControlDemo/RefreshControlDemo-Prefix.pch"; 341 | INFOPLIST_FILE = "RefreshControlDemo/RefreshControlDemo-Info.plist"; 342 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 343 | OTHER_LDFLAGS = "-ObjC"; 344 | PRODUCT_NAME = "$(TARGET_NAME)"; 345 | WRAPPER_EXTENSION = app; 346 | }; 347 | name = Debug; 348 | }; 349 | B716117C1631D4DA00B08595 /* Release */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 353 | GCC_PREFIX_HEADER = "RefreshControlDemo/RefreshControlDemo-Prefix.pch"; 354 | INFOPLIST_FILE = "RefreshControlDemo/RefreshControlDemo-Info.plist"; 355 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 356 | OTHER_LDFLAGS = "-ObjC"; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | WRAPPER_EXTENSION = app; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | B71611561631D4DA00B08595 /* Build configuration list for PBXProject "RefreshControlDemo" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | B71611781631D4DA00B08595 /* Debug */, 369 | B71611791631D4DA00B08595 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | B716117A1631D4DA00B08595 /* Build configuration list for PBXNativeTarget "RefreshControlDemo" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | B716117B1631D4DA00B08595 /* Debug */, 378 | B716117C1631D4DA00B08595 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = B71611531631D4DA00B08595 /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /RefreshControlDemo/en.lproj/MainStoryboard.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 | 44 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 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 | 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 | 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 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 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 | -------------------------------------------------------------------------------- /CKRefreshControl/CKRefreshControl.m: -------------------------------------------------------------------------------- 1 | // CKRefreshControl.m 2 | // 3 | // Copyright (c) 2012 Instructure, Inc. 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import "CKRefreshControl.h" 24 | #import "CKRefreshArrowView.h" 25 | #import "CKParagraphStyle.h" 26 | #import 27 | 28 | #if !__has_feature(objc_arc) 29 | #error Add -fobjc-arc to the compile flags for CKRefreshControl.m 30 | #endif 31 | 32 | typedef enum { 33 | CKRefreshControlStateHidden, 34 | CKRefreshControlStatePulling, 35 | CKRefreshControlStateReady, 36 | CKRefreshControlStateRefreshing 37 | } CKRefreshControlState; 38 | 39 | @interface CKRefreshControl () 40 | @property (nonatomic) CKRefreshControlState refreshControlState; 41 | @end 42 | 43 | @implementation CKRefreshControl { 44 | UILabel *textLabel; 45 | UIActivityIndicatorView *spinner; 46 | CKRefreshArrowView *arrow; 47 | UIColor *defaultTintColor; 48 | CGFloat originalTopContentInset; 49 | CGFloat decelerationStartOffset; 50 | } 51 | 52 | - (id)init 53 | { 54 | if (![[UIRefreshControl class] isSubclassOfClass:[CKRefreshControl class]]) 55 | return (id)[[UIRefreshControl alloc] init]; 56 | 57 | if (self = [super init]) 58 | { 59 | [self commonInit]; 60 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin; 61 | self.backgroundColor = [UIColor clearColor]; 62 | } 63 | return self; 64 | } 65 | 66 | - (id) initWithCoder:(NSCoder *)aDecoder 67 | { 68 | if (self = [super initWithCoder:aDecoder]) 69 | { 70 | [self commonInit]; 71 | 72 | if ([aDecoder containsValueForKey:@"UITintColor"]) 73 | { 74 | self.tintColor = (UIColor *)[aDecoder decodeObjectForKey:@"UITintColor"]; 75 | defaultTintColor = self.tintColor; 76 | } 77 | 78 | if ([aDecoder containsValueForKey:@"UIAttributedTitle"]) 79 | self.attributedTitle = [aDecoder decodeObjectForKey:@"UIAttributedTitle"]; 80 | 81 | // we can set its refresh control when the table view controller sets its view 82 | [[NSNotificationCenter defaultCenter] addObserver: self 83 | selector: @selector(tableViewControllerDidSetView:) 84 | name: CKRefreshControl_UITableViewController_DidSetView_Notification 85 | object: nil ]; 86 | 87 | } 88 | return self; 89 | } 90 | 91 | - (void) commonInit 92 | { 93 | self.frame = CGRectMake(0, 0, 320, 60); 94 | [self populateSubviews]; 95 | [self setRefreshControlState:CKRefreshControlStateHidden]; 96 | defaultTintColor = [UIColor colorWithWhite:0.5 alpha:1]; 97 | } 98 | 99 | - (void) tableViewControllerDidSetView: (NSNotification *) notification 100 | { 101 | [[NSNotificationCenter defaultCenter] removeObserver:self 102 | name:CKRefreshControl_UITableViewController_DidSetView_Notification 103 | object:nil]; 104 | 105 | UITableViewController *tableViewController = notification.object; 106 | if (tableViewController.refreshControl != (id)self) 107 | tableViewController.refreshControl = (id)self; 108 | } 109 | 110 | // remove notification observer in case notification never fired 111 | - (void) awakeFromNib 112 | { 113 | [[NSNotificationCenter defaultCenter] removeObserver: self 114 | name: CKRefreshControl_UITableViewController_DidSetView_Notification 115 | object: nil ]; 116 | [super awakeFromNib]; 117 | } 118 | 119 | - (void)populateSubviews { 120 | CGRect frame = CGRectInset(self.bounds, 12, 12); 121 | arrow = [[CKRefreshArrowView alloc] initWithFrame:frame]; 122 | arrow.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 123 | [self addSubview:arrow]; 124 | 125 | textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 126 | textLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 127 | textLabel.textAlignment = UITextAlignmentCenter; 128 | textLabel.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]]; 129 | textLabel.backgroundColor = [UIColor clearColor]; 130 | textLabel.textColor = [UIColor colorWithWhite:0.5 alpha:1]; 131 | [self addSubview:textLabel]; 132 | 133 | spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 134 | spinner.center = (CGPoint){ 135 | .x = CGRectGetMidX(self.bounds), 136 | .y = CGRectGetMidY(self.bounds) 137 | }; 138 | spinner.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | 139 | UIViewAutoresizingFlexibleRightMargin | 140 | UIViewAutoresizingFlexibleTopMargin | 141 | UIViewAutoresizingFlexibleBottomMargin); 142 | [self addSubview:spinner]; 143 | } 144 | 145 | - (void)setTintColor: (UIColor *) tintColor 146 | { 147 | if (!tintColor) 148 | tintColor = defaultTintColor; 149 | 150 | textLabel.textColor = tintColor; 151 | arrow.tintColor = tintColor; 152 | spinner.color = tintColor; 153 | } 154 | 155 | - (UIColor *)tintColor { 156 | return arrow.tintColor; 157 | } 158 | 159 | - (void)setAttributedTitle:(NSAttributedString *)attributedTitle { 160 | _attributedTitle = attributedTitle; 161 | textLabel.text = attributedTitle.string; 162 | } 163 | 164 | - (void)beginRefreshing { 165 | _refreshing = YES; 166 | [self setRefreshControlState:CKRefreshControlStateRefreshing]; 167 | } 168 | 169 | - (void)endRefreshing { 170 | _refreshing = NO; 171 | [self setRefreshControlState:CKRefreshControlStateHidden]; 172 | } 173 | 174 | - (void)layoutSubviews { 175 | [super layoutSubviews]; 176 | NSString *text = self.attributedTitle.string; 177 | CGPoint center =(CGPoint){ 178 | .x = CGRectGetMidX(self.bounds), 179 | .y = CGRectGetMidY(self.bounds) 180 | }; 181 | 182 | if (text.length > 0) { 183 | CGPoint newArrowCenter = (CGPoint){.x = center.x, .y = center.y - 10}; 184 | arrow.center = spinner.center = newArrowCenter; 185 | textLabel.frame = (CGRect){ 186 | .origin.x = 0, 187 | .origin.y = CGRectGetMaxY(self.bounds) - 25, 188 | .size.width = CGRectGetWidth(self.bounds), 189 | .size.height = 25 190 | }; 191 | } 192 | else { 193 | arrow.center = spinner.center = center; 194 | textLabel.frame = CGRectZero; 195 | } 196 | } 197 | 198 | - (void)setRefreshControlState:(CKRefreshControlState)refreshControlState { 199 | 200 | _refreshControlState = refreshControlState; 201 | switch (refreshControlState) { 202 | case CKRefreshControlStateHidden: 203 | { 204 | [UIView animateWithDuration:0.2 animations:^{ 205 | self.alpha = 0.0; 206 | }]; 207 | break; 208 | } 209 | 210 | case CKRefreshControlStatePulling: 211 | case CKRefreshControlStateReady: 212 | self.alpha = 1.0; 213 | arrow.alpha = 1.0; 214 | textLabel.alpha = 1.0; 215 | break; 216 | 217 | case CKRefreshControlStateRefreshing: 218 | self.alpha = 1.0; 219 | [UIView animateWithDuration: 0.2 220 | animations:^{ 221 | arrow.alpha = 0.0; 222 | } 223 | completion:^(BOOL finished) { 224 | [spinner startAnimating]; 225 | }]; 226 | break; 227 | }; 228 | 229 | 230 | 231 | UIEdgeInsets contentInset = UIEdgeInsetsMake(originalTopContentInset, 0, 0, 0); 232 | if (refreshControlState == CKRefreshControlStateRefreshing) { 233 | contentInset = UIEdgeInsetsMake(self.frame.size.height + originalTopContentInset, 0, 0, 0); 234 | } 235 | else { 236 | [spinner stopAnimating]; 237 | } 238 | 239 | 240 | UIScrollView *scrollView = nil; 241 | if ([self.superview isKindOfClass:[UIScrollView class]]) { 242 | scrollView = (UIScrollView *)self.superview; 243 | } 244 | 245 | if(!UIEdgeInsetsEqualToEdgeInsets(scrollView.contentInset, contentInset)) { 246 | [UIView animateWithDuration:0.2 animations:^{ 247 | scrollView.contentInset = contentInset; 248 | }]; 249 | } 250 | 251 | } 252 | 253 | static void *contentOffsetObservingKey = &contentOffsetObservingKey; 254 | 255 | - (void)willMoveToSuperview:(UIView *)newSuperview { 256 | [self.superview removeObserver:self forKeyPath:@"contentOffset" context:contentOffsetObservingKey]; 257 | } 258 | 259 | - (void)didMoveToSuperview { 260 | UIView *superview = self.superview; 261 | 262 | // Reposition ourself in the scrollview 263 | if ([superview isKindOfClass:[UIScrollView class]]) { 264 | [self repositionAboveContent]; 265 | 266 | [superview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionOld context:contentOffsetObservingKey]; 267 | 268 | originalTopContentInset = [(UIScrollView *)superview contentInset].top; 269 | } 270 | // Set the 'UITableViewController.refreshControl' property, if applicable 271 | if ([superview isKindOfClass:[UITableView class]]) { 272 | UITableViewController *tableViewController = (UITableViewController *)superview.nextResponder; 273 | if ([tableViewController isKindOfClass:[UITableViewController class]]) { 274 | if (tableViewController.refreshControl != (id)self) 275 | tableViewController.refreshControl = (id)self; 276 | } 277 | } 278 | } 279 | 280 | - (void)repositionAboveContent { 281 | CGRect scrollBounds = self.superview.bounds; 282 | CGFloat height = self.bounds.size.height; 283 | CGRect newFrame = (CGRect){ 284 | .origin.x = 0, 285 | .origin.y = -height, 286 | .size.width = scrollBounds.size.width, 287 | .size.height = height 288 | }; 289 | self.frame = newFrame; 290 | } 291 | 292 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 293 | if (context != contentOffsetObservingKey) { 294 | return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 295 | } 296 | 297 | if ([self.superview isKindOfClass:[UIScrollView class]] == NO) { 298 | return; 299 | } 300 | UIScrollView *scrollview = (UIScrollView *)self.superview; 301 | CGFloat pullHeight = -scrollview.contentOffset.y - originalTopContentInset; 302 | CGFloat triggerHeight = self.bounds.size.height; 303 | CGFloat previousPullHeight = -[change[NSKeyValueChangeOldKey] CGPointValue].y; 304 | 305 | // Update the progress arrow 306 | CGFloat progress = pullHeight / triggerHeight; 307 | CGFloat deadZone = 0.3; 308 | if (progress > deadZone) { 309 | CGFloat arrowProgress = ((progress - deadZone) / (1 - deadZone)); 310 | arrow.progress = arrowProgress; 311 | } 312 | else { 313 | arrow.progress = 0.0; 314 | } 315 | 316 | 317 | // Track when deceleration starts 318 | if (scrollview.isDecelerating == NO) { 319 | decelerationStartOffset = 0; 320 | } 321 | else if (scrollview.isDecelerating && decelerationStartOffset == 0) { 322 | decelerationStartOffset = scrollview.contentOffset.y; 323 | } 324 | 325 | 326 | // Transition to the next state 327 | if (self.refreshControlState == CKRefreshControlStateRefreshing) { 328 | // Adjust inset to make sure potential header view is shown correctly if user pulls down scroll view while in refreshing state 329 | CGFloat offset = MAX(scrollview.contentOffset.y * -1, 0); 330 | offset = MIN(offset, self.bounds.size.height); 331 | scrollview.contentInset = UIEdgeInsetsMake(offset, 0.0f, 0.0f, 0.0f); 332 | } 333 | else if (decelerationStartOffset > 0) { 334 | // Deceleration started before reaching the header 'rubber band' area; hide the refresh control 335 | self.refreshControlState = CKRefreshControlStateHidden; 336 | } 337 | else if (pullHeight >= triggerHeight || (pullHeight > 0 && previousPullHeight >= triggerHeight)) { 338 | 339 | if (scrollview.isDragging) { 340 | // Just waiting for them to let go, then we'll refresh 341 | self.refreshControlState = CKRefreshControlStateReady; 342 | } 343 | else if (([self allControlEvents] & UIControlEventValueChanged) == 0) { 344 | NSLog(@"No action configured for UIControlEventValueChanged event, not transitioning to refreshing state"); 345 | } 346 | else { 347 | // They let go! Refresh! 348 | self.refreshControlState = CKRefreshControlStateRefreshing; 349 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 350 | } 351 | } 352 | else if (scrollview.decelerating == NO && pullHeight > 0) { 353 | self.refreshControlState = CKRefreshControlStatePulling; 354 | } 355 | else { 356 | self.refreshControlState = CKRefreshControlStateHidden; 357 | } 358 | 359 | if (pullHeight > self.bounds.size.height) { 360 | // Center in the rubberbanding area 361 | CGPoint rubberBandCenter = (CGPoint) { 362 | .x = CGRectGetMidX(self.superview.bounds), 363 | .y = scrollview.contentOffset.y / 2.0 364 | }; 365 | self.center = rubberBandCenter; 366 | } 367 | else { 368 | [self repositionAboveContent]; 369 | } 370 | } 371 | 372 | #pragma mark - Class methods 373 | 374 | + (Class) class 375 | { 376 | // cannot call +class from inside this method 377 | Class uiRefreshControlClass = NSClassFromString(@"UIRefreshControl"); 378 | Class ckRefreshControlClass = NSClassFromString(@"CKRefreshControl"); 379 | 380 | if (![self isSubclassOfClass:ckRefreshControlClass]) 381 | return uiRefreshControlClass; 382 | 383 | return [super class]; 384 | } 385 | 386 | // If UIRefreshControl is available, we need to customize that class, not 387 | // CKRefreshControl. Otherwise, the +appearance proxy is broken on iOS 6. 388 | + (id)appearance 389 | { 390 | if (![[UIRefreshControl class] isSubclassOfClass:[CKRefreshControl class]]) 391 | return (id)[UIRefreshControl appearance]; 392 | 393 | if ([self isEqual:[UIRefreshControl class]]) 394 | return [CKRefreshControl appearance]; 395 | 396 | return [super appearance]; 397 | } 398 | 399 | + (id)appearanceWhenContainedIn:(Class)ContainerClass, ... 400 | { 401 | va_list list; 402 | va_start(list, ContainerClass); 403 | 404 | Class classes[10] = {0}; 405 | 406 | for (int i=0; i<10; ++i) { 407 | Class c = va_arg(list, Class); 408 | if (c == Nil) { 409 | break; 410 | } 411 | classes[i] = c; 412 | } 413 | va_end(list); 414 | 415 | if (![[UIRefreshControl class] isSubclassOfClass:[CKRefreshControl class]]) 416 | return (id)[UIRefreshControl appearanceWhenContainedIn:ContainerClass, classes[0], classes[1], classes[2], classes[3], classes[4], classes[5], classes[6], classes[7], classes[8], classes[9], nil]; 417 | 418 | if ([self isEqual:[UIRefreshControl class]]) 419 | return [CKRefreshControl appearanceWhenContainedIn:ContainerClass, classes[0], classes[1], classes[2], classes[3], classes[4], classes[5], classes[6], classes[7], classes[8], classes[9], nil]; 420 | 421 | return [super appearanceWhenContainedIn:ContainerClass, classes[0], classes[1], classes[2], classes[3], classes[4], classes[5], classes[6], classes[7], classes[8], classes[9], nil]; 422 | } 423 | 424 | #if __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_5_1 425 | #define IMP_WITH_BLOCK_TYPE __bridge void* 426 | #else 427 | #define IMP_WITH_BLOCK_TYPE id 428 | #endif 429 | 430 | typedef IMP *IMPPointer; 431 | 432 | static void *CKRefreshControlKey; 433 | 434 | static BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store) 435 | { 436 | IMP imp = NULL; 437 | Method method = class_getInstanceMethod(class, original); 438 | 439 | if (method) 440 | { 441 | const char *type = method_getTypeEncoding(method); 442 | imp = class_replaceMethod(class, original, replacement, type); 443 | 444 | if (!imp) 445 | imp = method_getImplementation(method); 446 | } 447 | 448 | if (imp && store) 449 | *store = imp; 450 | 451 | return (imp != NULL); 452 | } 453 | 454 | NSString *const CKRefreshControl_UITableViewController_DidSetView_Notification = @"CKRefreshControl_UITableViewController_DidSetView"; 455 | static void CKRefreshControl_UITableViewController_SetView(UITableViewController *dynamicSelf, SEL _cmd, UITableView *view); 456 | static void (*UITableViewController_SetViewIMP)(UITableViewController *dynamicSelf, SEL _cmd, UITableView *view); 457 | static void CKRefreshControl_UITableViewController_SetView(UITableViewController *dynamicSelf, SEL _cmd, UITableView *view) 458 | { 459 | UITableViewController_SetViewIMP(dynamicSelf, _cmd, view); 460 | [[NSNotificationCenter defaultCenter] postNotificationName:CKRefreshControl_UITableViewController_DidSetView_Notification object:dynamicSelf]; 461 | } 462 | 463 | 464 | // +load has some inline asm. When compiling a reference to a class name (e.g. UIRefreshControl), 465 | // the compiler emits "L_OBJC_CLASS_UIRefreshControl". If this label is nil or doesn't exist, the 466 | // class reference will be Nil. So we need to manually set the value at that label, and then 467 | // register the class with the runtime. That's all that's going on here. For a line-by-line 468 | // breakdown of what's going on, see https://gist.github.com/OliverLetterer/4643294/ 469 | 470 | + (void)load 471 | { 472 | if (objc_getClass("UIRefreshControl")) 473 | return; 474 | 475 | // CKRefreshControl will masquerade as UIRefreshControl 476 | static dispatch_once_t registerUIRefreshControlClass_onceToken; 477 | dispatch_once(®isterUIRefreshControlClass_onceToken, ^{ 478 | 479 | Class *UIRefreshControlClassRef = NULL; 480 | #if TARGET_CPU_ARM 481 | __asm( 482 | "movw %0, :lower16:(L_OBJC_CLASS_UIRefreshControl-(LPC0+4))\n" 483 | "movt %0, :upper16:(L_OBJC_CLASS_UIRefreshControl-(LPC0+4))\n" 484 | "LPC0: add %0, pc" : "=r"(UIRefreshControlClassRef) 485 | ); 486 | #elif TARGET_CPU_X86_64 487 | __asm("leaq L_OBJC_CLASS_UIRefreshControl(%%rip), %0" : "=r"(UIRefreshControlClassRef)); 488 | #elif TARGET_CPU_X86 489 | void *pc = NULL; 490 | __asm( 491 | "calll L0\n" 492 | "L0: popl %0\n" 493 | "leal L_OBJC_CLASS_UIRefreshControl-L0(%0), %1" : "=r"(pc), "=r"(UIRefreshControlClassRef) 494 | ); 495 | #else 496 | #error Unsupported CPU 497 | #endif 498 | if (UIRefreshControlClassRef && *UIRefreshControlClassRef == Nil) 499 | { 500 | *UIRefreshControlClassRef = objc_allocateClassPair(self, "UIRefreshControl", 0); 501 | objc_registerClassPair(*UIRefreshControlClassRef); 502 | } 503 | 504 | // Add UITableViewController.refreshControl if it isn't present 505 | Class tableViewController = [UITableViewController class]; 506 | IMP refreshControlIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^id(id dynamicSelf) { 507 | return objc_getAssociatedObject(dynamicSelf, &CKRefreshControlKey); 508 | })); 509 | BOOL added = class_addMethod(tableViewController, @selector(refreshControl), refreshControlIMP, "@@:"); 510 | 511 | NSAssert(added, @"We tried to add the refreshControl method, and it failed. This is going to break things, so we may as well stop here."); 512 | 513 | 514 | IMP setRefreshControlIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^void(UITableViewController *dynamicSelf, id newControl) { 515 | if (dynamicSelf.refreshControl == newControl) { 516 | return; 517 | } 518 | objc_setAssociatedObject(dynamicSelf, &CKRefreshControlKey, newControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 519 | 520 | if ([newControl superview] != dynamicSelf.tableView) { 521 | [dynamicSelf.tableView addSubview:newControl]; 522 | } 523 | })); 524 | 525 | added = class_addMethod(tableViewController, @selector(setRefreshControl:), setRefreshControlIMP, "v@:@"); 526 | NSAssert(added, @"We tried to add the setRefreshControl: method, and it failed. This is going to break things, so we may as well stop here."); 527 | 528 | // swizzle setView: method so we can post notification to CKRefreshControl ... 529 | // CKRefreshControl will assign itself to the UIViewController 530 | class_swizzleMethodAndStore(tableViewController, 531 | @selector(setView:), 532 | (IMP)CKRefreshControl_UITableViewController_SetView, 533 | (IMP *)&UITableViewController_SetViewIMP ); 534 | }); 535 | } 536 | 537 | // This is just declaring the L_OBJC_CLASS_UIRefreshControl label, so that we can reference it in +load. 538 | // Again, see https://gist.github.com/OliverLetterer/4643294/ for more information. 539 | 540 | __asm( 541 | ".section __DATA,__objc_classrefs,regular,no_dead_strip\n" 542 | #if TARGET_RT_64_BIT 543 | ".align 3\n" 544 | "L_OBJC_CLASS_UIRefreshControl:\n" 545 | ".quad _OBJC_CLASS_$_UIRefreshControl\n" 546 | #else 547 | ".align 2\n" 548 | "L_OBJC_CLASS_UIRefreshControl:\n" 549 | ".long _OBJC_CLASS_$_UIRefreshControl\n" 550 | #endif 551 | ".weak_reference _OBJC_CLASS_$_UIRefreshControl\n" 552 | ); 553 | 554 | @end 555 | --------------------------------------------------------------------------------