├── PongRefreshControlDemo ├── Podfile ├── PongRefreshControlDemo │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── BOZAppDelegate.h │ ├── main.m │ ├── BOZDemoViewController.h │ ├── PongRefreshControlDemo-Prefix.pch │ ├── BOZNoNavBarViewController.h │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── BOZRegularViewController.h │ ├── BOZScrollViewController.h │ ├── PongRefreshControlDemo-Info.plist │ ├── BOZNoNavBarViewController.xib │ ├── BOZNoNavBarViewController.m │ ├── BOZDemoViewController.m │ ├── BOZAppDelegate.m │ ├── BOZRegularViewController.m │ ├── BOZScrollViewController.m │ └── Storyboard.storyboard ├── PongRefreshControlDemoTests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── PongRefreshControlDemoTests-Info.plist │ └── PongRefreshControlDemoTests.m └── PongRefreshControlDemo.xcodeproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ └── project.pbxproj ├── .gitignore ├── BOZPongRefreshControl.podspec ├── LICENSE ├── README.md └── BOZPongRefreshControl ├── BOZPongRefreshControl.h └── BOZPongRefreshControl.m /PongRefreshControlDemo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios 2 | 3 | podspec :path => "../BOZPongRefreshControl.podspec" 4 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // BOZAppDelegate.h 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BOZAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "BOZAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([BOZAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZDemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BOZDemoViewController.h 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BOZPongRefreshControl.h" 11 | 12 | @interface BOZDemoViewController : UITableViewController 13 | 14 | @property (strong, nonatomic) BOZPongRefreshControl* pongRefreshControl; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/PongRefreshControlDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZNoNavBarViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BOZNoNavBarViewController.h 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BOZPongRefreshControl.h" 11 | 12 | @interface BOZNoNavBarViewController : UITableViewController 13 | 14 | @property (strong, nonatomic) BOZPongRefreshControl* pongRefreshControl; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZRegularViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BOZRegularViewController.h 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/7/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BOZPongRefreshControl.h" 11 | 12 | @interface BOZRegularViewController : UIViewController 13 | 14 | @property (weak, nonatomic) IBOutlet UITableView* tableView; 15 | 16 | @property (strong, nonatomic) BOZPongRefreshControl* pongRefreshControl; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BOZScrollViewController.h 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/7/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BOZPongRefreshControl.h" 11 | 12 | @interface BOZScrollViewController : UIViewController { 13 | UILabel* scrollMeLabel; 14 | } 15 | 16 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; 17 | 18 | @property (strong, nonatomic) BOZPongRefreshControl* pongRefreshControl; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemoTests/PongRefreshControlDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.boztalay.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BOZPongRefreshControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'BOZPongRefreshControl' 3 | s.version = '1.0.1' 4 | s.summary = 'A pull-down-to-refresh control for iOS that plays pong, originally created for the MHacks III iOS app' 5 | s.homepage = 'https://github.com/boztalay/BOZPongRefreshControl' 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.authors = { 'Ben Oztalay' => 'boztalay@gmail.com', 'Joseph Constan' => 'jcon5294@gmail.com', 'Mahir Eusufzai' => 'github.com/MahirEusufzai', 'ajfigueroa' => 'github.com/ajfigueroa'} 8 | s.platform = :ios 9 | s.source = { :git => 'https://github.com/boztalay/BOZPongRefreshControl.git', :tag => '1.0.1' } 10 | s.source_files = 'BOZPongRefreshControl/**/*.{h,m}' 11 | s.public_header_files = 'BOZPongRefreshControl/**/*.h' 12 | s.framework = 'UIKit' 13 | s.requires_arc = true 14 | end 15 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemoTests/PongRefreshControlDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PongRefreshControlDemoTests.m 3 | // PongRefreshControlDemoTests 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PongRefreshControlDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation PongRefreshControlDemoTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Ben Oztalay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/PongRefreshControlDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.boztalay.${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 | Storyboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeRight 37 | UIInterfaceOrientationLandscapeLeft 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZNoNavBarViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZNoNavBarViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BOZNoNavBarViewController.m 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import "BOZNoNavBarViewController.h" 10 | 11 | @implementation BOZNoNavBarViewController 12 | 13 | - (void)viewDidLoad 14 | { 15 | [super viewDidLoad]; 16 | 17 | /* NOTE: Do NOT attach the refresh control in viewDidLoad! 18 | * If you do this here, it'll act very funny if you have 19 | * a navigation bar or other such similar thing that iOS 7 20 | * automatically offsets content for. You have to wait for 21 | * the subviews to get laid out first so the refresh 22 | * control knows how big that offset is! 23 | */ 24 | } 25 | 26 | - (void)viewDidLayoutSubviews 27 | { 28 | self.pongRefreshControl = [BOZPongRefreshControl attachToTableView:self.tableView 29 | withRefreshTarget:self 30 | andRefreshAction:@selector(refreshTriggered)]; 31 | self.pongRefreshControl.foregroundColor = [UIColor greenColor]; 32 | } 33 | 34 | //Resetting the refresh control if the user leaves the screen 35 | - (void)viewWillDisappear:(BOOL)animated 36 | { 37 | [self.pongRefreshControl finishedLoading]; 38 | } 39 | 40 | #pragma mark - Notifying the pong refresh control of scrolling 41 | 42 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 43 | { 44 | [self.pongRefreshControl scrollViewDidScroll]; 45 | } 46 | 47 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 48 | { 49 | [self.pongRefreshControl scrollViewDidEndDragging]; 50 | } 51 | 52 | #pragma mark - Listening for the user to trigger a refresh 53 | 54 | - (void)refreshTriggered 55 | { 56 | 57 | } 58 | 59 | #pragma mark - Resetting the refresh control when loading is done 60 | 61 | - (IBAction)doneLoadingButtonPressed:(id)sender 62 | { 63 | [self.pongRefreshControl finishedLoading]; 64 | } 65 | 66 | #pragma mark - Going back 67 | 68 | - (IBAction)goBackButtonPressed:(id)sender 69 | { 70 | [self dismissViewControllerAnimated:YES completion:NULL]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZDemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BOZDemoViewController.m 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import "BOZDemoViewController.h" 10 | 11 | @implementation BOZDemoViewController 12 | 13 | - (void)viewDidLoad 14 | { 15 | [super viewDidLoad]; 16 | 17 | /* NOTE: Do NOT attach the refresh control in viewDidLoad! 18 | * If you do this here, it'll act very funny if you have 19 | * a navigation bar or other such similar thing that iOS 7 20 | * automatically offsets content for. You have to wait for 21 | * the subviews to get laid out first so the refresh 22 | * control knows how big that offset is! 23 | */ 24 | } 25 | 26 | - (void)viewDidLayoutSubviews 27 | { 28 | self.pongRefreshControl = [BOZPongRefreshControl attachToTableView:self.tableView 29 | withRefreshTarget:self 30 | andRefreshAction:@selector(refreshTriggered)]; 31 | } 32 | 33 | - (void)viewWillDisappear:(BOOL)animated 34 | { 35 | //Resetting the refresh control if the user leaves the screen 36 | [self.pongRefreshControl finishedLoading]; 37 | } 38 | 39 | #pragma mark - Notifying the pong refresh control of scrolling 40 | 41 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 42 | { 43 | [self.pongRefreshControl scrollViewDidScroll]; 44 | } 45 | 46 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 47 | { 48 | [self.pongRefreshControl scrollViewDidEndDragging]; 49 | } 50 | 51 | #pragma mark - Listening for the user to trigger a refresh 52 | 53 | - (void)refreshTriggered 54 | { 55 | // UIAlertView *alert = [[UIAlertView alloc] 56 | // initWithTitle:@"Whoa!" 57 | // message:@"You triggered a refresh! Hit \"Done Loading\" to make it stop." 58 | // delegate:nil 59 | // cancelButtonTitle:@"Sweet!" 60 | // otherButtonTitles:nil]; 61 | // [alert show]; 62 | } 63 | 64 | #pragma mark - Resetting the refresh control when loading is done 65 | 66 | - (IBAction)doneLoadingButtonPressed:(id)sender 67 | { 68 | [self.pongRefreshControl finishedLoading]; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // BOZAppDelegate.m 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/6/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import "BOZAppDelegate.h" 10 | 11 | @implementation BOZAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | 17 | UIStoryboard* storyboard = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:@"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]]; 18 | self.window.rootViewController = [storyboard instantiateInitialViewController]; 19 | 20 | self.window.backgroundColor = [UIColor whiteColor]; 21 | [self.window makeKeyAndVisible]; 22 | return YES; 23 | } 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application 26 | { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application 32 | { 33 | // 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. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application 38 | { 39 | // 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. 40 | } 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application 43 | { 44 | // 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. 45 | } 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application 48 | { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZRegularViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BOZRegularViewController.m 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/7/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import "BOZRegularViewController.h" 10 | 11 | @implementation BOZRegularViewController 12 | 13 | - (void)viewDidLoad 14 | { 15 | [super viewDidLoad]; 16 | 17 | /* NOTE: Do NOT attach the refresh control in viewDidLoad! 18 | * If you do this here, it'll act very funny if you have 19 | * a navigation bar or other such similar thing that iOS 7 20 | * automatically offsets content for. You have to wait for 21 | * the subviews to get laid out first so the refresh 22 | * control knows how big that offset is! 23 | */ 24 | } 25 | 26 | - (void)viewDidLayoutSubviews 27 | { 28 | self.pongRefreshControl = [BOZPongRefreshControl attachToTableView:self.tableView 29 | withRefreshTarget:self 30 | andRefreshAction:@selector(refreshTriggered)]; 31 | self.pongRefreshControl.backgroundColor = [UIColor whiteColor]; 32 | self.pongRefreshControl.foregroundColor = [UIColor blackColor]; 33 | } 34 | 35 | //Resetting the refresh control if the user leaves the screen 36 | - (void)viewWillDisappear:(BOOL)animated 37 | { 38 | [self.pongRefreshControl finishedLoading]; 39 | } 40 | 41 | #pragma mark - Notifying the pong refresh control of scrolling 42 | 43 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 44 | { 45 | [self.pongRefreshControl scrollViewDidScroll]; 46 | } 47 | 48 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 49 | { 50 | [self.pongRefreshControl scrollViewDidEndDragging]; 51 | } 52 | 53 | #pragma mark - Listening for the user to trigger a refresh 54 | 55 | - (void)refreshTriggered 56 | { 57 | 58 | } 59 | 60 | #pragma mark - Resetting the refresh control when loading is done 61 | 62 | - (IBAction)doneLoadingButtonPressed:(id)sender 63 | { 64 | [self.pongRefreshControl finishedLoading]; 65 | } 66 | 67 | #pragma mark - Table view data source 68 | 69 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 70 | { 71 | return 7; 72 | } 73 | 74 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 75 | { 76 | static NSString *CellIdentifier = @"Cell"; 77 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 78 | if (cell == nil) { 79 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 80 | } 81 | 82 | CGFloat alphaComponent = 1.0f - (float)(indexPath.row + 1) / (float)([tableView numberOfRowsInSection:0] + 1); 83 | cell.contentView.backgroundColor = [UIColor colorWithRed:0.373f green:0.820f blue:0.294f alpha:alphaComponent]; 84 | cell.backgroundColor = [UIColor whiteColor]; 85 | 86 | return cell; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BOZPongRefreshControl 2 | ===================== 3 | 4 | A pull-down-to-refresh control for iOS that plays pong 5 | 6 |

7 | 8 | Installation 9 | ------------ 10 | 11 | It's on CocoaPods! Put ```pod 'BOZPongRefreshControl'``` in your Podfile. 12 | 13 | Alternatively, just place ```BOZPongRefreshControl.h``` and ```BOZPongRefreshControl.m``` in your project anywhere you'd like. 14 | 15 | Usage 16 | -------- 17 | 18 | Attach it to a ```UITableView``` or ```UIScrollView``` like so: 19 | 20 | ```objective-c 21 | - (void)viewDidLoad 22 | { 23 | [super viewDidLoad]; 24 | 25 | /* NOTE: Do NOT attach the refresh control in viewDidLoad! 26 | * If you do this here, it'll act very funny if you have 27 | * a navigation bar or other such similar thing that iOS 28 | * automatically offsets content for. You have to wait for 29 | * the subviews to get laid out first so the refresh 30 | * control knows how big that offset is! 31 | */ 32 | } 33 | 34 | - (void)viewDidLayoutSubviews 35 | { 36 | self.pongRefreshControl = [BOZPongRefreshControl attachToTableView:self.tableView 37 | withRefreshTarget:self 38 | andRefreshAction:@selector(refreshTriggered)]; 39 | } 40 | ``` 41 | 42 | Then, implement ```UIScrollViewDelegate``` in your ```UIViewController``` if you haven't already, and pass the calls through to the refresh control: 43 | 44 | ```objective-c 45 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 46 | { 47 | [self.pongRefreshControl scrollViewDidScroll]; 48 | } 49 | 50 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 51 | { 52 | [self.pongRefreshControl scrollViewDidEndDragging]; 53 | } 54 | ``` 55 | 56 | Lastly, make sure you've implemented the ```refreshAction``` you passed it earlier to listen for refresh triggers: 57 | 58 | ```objective-c 59 | - (void)refreshTriggered 60 | { 61 | //Go and load some data 62 | 63 | //Finshed loading the data, reset the refresh control 64 | [self.pongRefreshControl finishedLoading]; 65 | } 66 | ``` 67 | 68 | For more details, check out the demo app's code. It has examples for using the refresh control on a ```UIScrollView``` and outside of a ```UITableViewController```. 69 | 70 | Configuration 71 | ------------- 72 | 73 | - Set the foreground color with the ```foregroundColor``` property 74 | - Set the background color with the ```backgroundColor``` property 75 | - Adjust how fast it plays by changing the ```totalHorizontalTravelTimeForBall``` property 76 | 77 | Known Issues/To Do 78 | ------------------ 79 | 80 | - It'll interfere with ```UIScrollView``` content that's above ```y = 0.0f``` 81 | - I haven't tested it, but I'd be willing to bet it looks a bit silly on iPads 82 | - Test it out on a physical iPhone 6+ 83 | - The behavior of the paddles needs a little work 84 | - The the ```UIScrollView``` that it's attached to is scrolled automatically, the refresh control may be scrolled down into view under some circumstances (expanding/collapsing ```UITableViewCell```s, for example) 85 | - Tests! 86 | -------------------------------------------------------------------------------- /BOZPongRefreshControl/BOZPongRefreshControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // BOZPongRefreshControl.h 3 | // Ben Oztalay 4 | // 5 | // Created by Ben Oztalay on 11/22/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | // Version 1.0.0 9 | // https://www.github.com/boztalay/BOZPongRefreshControl 10 | // 11 | 12 | #import 13 | 14 | @interface BOZPongRefreshControl : UIView 15 | 16 | #pragma mark - Attaching a pong refresh control 17 | 18 | /** 19 | * This function simply calls attachToScrollView. Kept for 20 | * compatibility with earlier versions. 21 | */ 22 | + (BOZPongRefreshControl*)attachToTableView:(UITableView*)tableView 23 | withRefreshTarget:(id)refreshTarget 24 | andRefreshAction:(SEL)refreshAction; 25 | 26 | /** 27 | * Call this function to attach a pong refresh control to 28 | * a UIScrollView. Keep in mind that it does this by adding 29 | * the pong refresh control as a subview above the normal 30 | * content frame (negative y value in the origin), so if you 31 | * have content up there, it'll be covered up. 32 | * 33 | * The pong refresh control will perform the refreshAction on 34 | * on the refreshTarget when the user triggers a refresh. 35 | */ 36 | + (BOZPongRefreshControl*)attachToScrollView:(UIScrollView*)scrollView 37 | withRefreshTarget:(id)refreshTarget 38 | andRefreshAction:(SEL)refreshAction; 39 | 40 | #pragma mark - Functions required to use a pong refresh control 41 | 42 | /** 43 | * Calls [self beginLoadingAnimated:YES]. Kept for 44 | * backwards-compatibility. 45 | */ 46 | - (void)beginLoading; 47 | 48 | /** 49 | * Call this function to programatically scroll the refresh 50 | * control into view, and begin the animation. Does not notify 51 | * target of trigger. 52 | * @param animated Dictates whether the action of scrolling to 53 | * the refresh control is animated (YES) or instant (NO). 54 | */ 55 | - (void)beginLoadingAnimated:(BOOL)animated; 56 | 57 | /** 58 | * Call this function when whatever loading task you're doing 59 | * is done. This will reset the pong refresh control and hide 60 | * it. It's also a good idea to call this if your view is 61 | * going to disappear. 62 | */ 63 | - (void)finishedLoading; 64 | 65 | /** 66 | * Override the implementation of scrollViewDidScroll: in 67 | * UIScrollViewDelegate and call this function inside of it. 68 | * This lets the pong refresh control know to update its 69 | * subviews as the user scrolls. 70 | */ 71 | - (void)scrollViewDidScroll; 72 | 73 | /** 74 | * Override the implementation of scrollViewDidEndDragging:willDecelerate: 75 | * in UIScrollViewDelegate and call this function inside of it. 76 | * This lets the pong refresh control know the user let go 77 | * of the scroll view and causes it to check if a refresh should 78 | * be triggered. 79 | */ 80 | - (void)scrollViewDidEndDragging; 81 | 82 | #pragma mark - Configurable properties 83 | 84 | /** 85 | * This controls the color of the paddles and ball. Use 86 | * the standard backgroundColor property to set the 87 | * background color. 88 | */ 89 | @property (strong, nonatomic) UIColor* foregroundColor; 90 | 91 | /** 92 | * This controls how long it takes the ball to get from 93 | * one paddle to the other, regardless of floor/ceiling 94 | * bouncing 95 | */ 96 | @property (nonatomic) CGFloat totalHorizontalTravelTimeForBall; 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/BOZScrollViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BOZScrollViewController.m 3 | // PongRefreshControlDemo 4 | // 5 | // Created by Ben Oztalay on 12/7/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | 9 | #import "BOZScrollViewController.h" 10 | 11 | #define STRIPE_HEIGHT 50.0f 12 | #define NUM_STRIPES 11 13 | #define SCROLLVIEW_CONTENT_HEIGHT (STRIPE_HEIGHT * NUM_STRIPES) 14 | 15 | @implementation BOZScrollViewController 16 | 17 | - (void)viewDidLoad 18 | { 19 | [super viewDidLoad]; 20 | 21 | self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, SCROLLVIEW_CONTENT_HEIGHT); 22 | 23 | for(int i = 0; i < NUM_STRIPES; i++) { 24 | UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, i * STRIPE_HEIGHT, self.scrollView.frame.size.width, STRIPE_HEIGHT)]; 25 | 26 | CGFloat proportionOfScrollViewFilled = ((float)(i + 1) / (NUM_STRIPES + 1.0f)); 27 | CGFloat whiteValue = 1.0f - proportionOfScrollViewFilled; 28 | 29 | view.backgroundColor = [UIColor colorWithWhite:whiteValue alpha:1.0f]; 30 | 31 | if(i == 0) { 32 | scrollMeLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 33 | scrollMeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 34 | [view addSubview:scrollMeLabel]; 35 | [self setScrollMeLabelText:@"Scroll Me!"]; 36 | } 37 | 38 | view.autoresizingMask = UIViewAutoresizingFlexibleWidth; 39 | [self.scrollView addSubview:view]; 40 | } 41 | 42 | /* NOTE: Do NOT attach the refresh control in viewDidLoad! 43 | * If you do this here, it'll act very funny if you have 44 | * a navigation bar or other such similar thing that iOS 7 45 | * automatically offsets content for. You have to wait for 46 | * the subviews to get laid out first so the refresh 47 | * control knows how big that offset is! 48 | */ 49 | } 50 | 51 | - (void)setScrollMeLabelText:(NSString*)newText 52 | { 53 | scrollMeLabel.text = newText; 54 | [scrollMeLabel sizeToFit]; 55 | scrollMeLabel.center = CGPointMake(scrollMeLabel.superview.frame.size.width / 2.0f, 56 | scrollMeLabel.superview.frame.size.height / 2.0f); 57 | } 58 | 59 | - (void)viewDidLayoutSubviews 60 | { 61 | self.pongRefreshControl = [BOZPongRefreshControl attachToScrollView:self.scrollView 62 | withRefreshTarget:self 63 | andRefreshAction:@selector(refreshTriggered)]; 64 | self.pongRefreshControl.backgroundColor = [UIColor colorWithRed:0.000f green:0.132f blue:0.298f alpha:1.0f]; 65 | self.pongRefreshControl.foregroundColor = [UIColor colorWithRed:1.000f green:0.796f blue:0.020f alpha:1.0f]; 66 | } 67 | 68 | //Resetting the refresh control if the user leaves the screen 69 | - (void)viewWillDisappear:(BOOL)animated 70 | { 71 | [self.pongRefreshControl finishedLoading]; 72 | } 73 | 74 | 75 | #pragma mark - Notifying the pong refresh control of scrolling 76 | 77 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 78 | { 79 | [self.pongRefreshControl scrollViewDidScroll]; 80 | } 81 | 82 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 83 | { 84 | [self.pongRefreshControl scrollViewDidEndDragging]; 85 | 86 | [self setScrollMeLabelText:@"Scroll Me!"]; 87 | } 88 | 89 | #pragma mark - Listening for the user to trigger a refresh 90 | 91 | - (void)refreshTriggered 92 | { 93 | 94 | } 95 | 96 | #pragma mark - Resetting the refresh control when loading is done 97 | 98 | - (IBAction)doneLoadingButtonPressed:(id)sender 99 | { 100 | [self.pongRefreshControl finishedLoading]; 101 | } 102 | 103 | #pragma mark - Changing the "Scroll Me!" label 104 | 105 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 106 | { 107 | [self setScrollMeLabelText:@":D"]; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo/Storyboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /BOZPongRefreshControl/BOZPongRefreshControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // BOZPongRefreshControl.m 3 | // Ben Oztalay 4 | // 5 | // Created by Ben Oztalay on 11/22/13. 6 | // Copyright (c) 2013 Ben Oztalay. All rights reserved. 7 | // 8 | // Version 1.0.0 9 | // https://www.github.com/boztalay/BOZPongRefreshControl 10 | // 11 | 12 | #import "BOZPongRefreshControl.h" 13 | 14 | #define REFRESH_CONTROL_HEIGHT 65.0f 15 | #define HALF_REFRESH_CONTROL_HEIGHT (REFRESH_CONTROL_HEIGHT / 2.0f) 16 | 17 | #define DEFAULT_FOREGROUND_COLOR [UIColor whiteColor] 18 | #define DEFAULT_BACKGROUND_COLOR [UIColor colorWithWhite:0.10f alpha:1.0f] 19 | 20 | #define DEFAULT_TOTAL_HORIZONTAL_TRAVEL_TIME_FOR_BALL 0.75f 21 | 22 | #define TRANSITION_ANIMATION_DURATION 0.2f 23 | 24 | typedef enum { 25 | BOZPongRefreshControlStateIdle = 0, 26 | BOZPongRefreshControlStateRefreshing = 1, 27 | BOZPongRefreshControlStateResetting = 2 28 | } BOZPongRefreshControlState; 29 | 30 | @interface BOZPongRefreshControl() { 31 | BOZPongRefreshControlState state; 32 | 33 | CGFloat originalTopContentInset; 34 | 35 | UIView* leftPaddleView; 36 | UIView* rightPaddleView; 37 | UIView* ballView; 38 | 39 | CGPoint leftPaddleIdleOrigin; 40 | CGPoint rightPaddleIdleOrigin; 41 | CGPoint ballIdleOrigin; 42 | 43 | CGPoint ballOrigin; 44 | CGPoint ballDestination; 45 | CGPoint ballDirection; 46 | 47 | CGFloat leftPaddleOrigin; 48 | CGFloat rightPaddleOrigin; 49 | CGFloat leftPaddleDestination; 50 | CGFloat rightPaddleDestination; 51 | 52 | UIView* gameView; 53 | 54 | NSDate* currentAnimationStartTime; 55 | CGFloat currentAnimationDuration; 56 | } 57 | 58 | @property (assign, nonatomic) UIScrollView* scrollView; 59 | @property (assign, nonatomic) id refreshTarget; 60 | @property (nonatomic) SEL refreshAction; 61 | @property (nonatomic, readonly) CGFloat distanceScrolled; 62 | 63 | @end 64 | 65 | @implementation BOZPongRefreshControl 66 | 67 | #pragma mark - Attaching a pong refresh control to a UIScrollView or UITableView 68 | 69 | #pragma mark UITableView 70 | 71 | + (BOZPongRefreshControl*)attachToTableView:(UITableView*)tableView 72 | withRefreshTarget:(id)refreshTarget 73 | andRefreshAction:(SEL)refreshAction 74 | { 75 | return [self attachToScrollView:tableView 76 | withRefreshTarget:refreshTarget 77 | andRefreshAction:refreshAction]; 78 | } 79 | 80 | #pragma mark UIScrollView 81 | 82 | + (BOZPongRefreshControl*)attachToScrollView:(UIScrollView*)scrollView 83 | withRefreshTarget:(id)refreshTarget 84 | andRefreshAction:(SEL)refreshAction 85 | { 86 | BOZPongRefreshControl* existingPongRefreshControl = [self findPongRefreshControlInScrollView:scrollView]; 87 | if(existingPongRefreshControl != nil) { 88 | return existingPongRefreshControl; 89 | } 90 | 91 | //Initialized height to 0 to hide it 92 | BOZPongRefreshControl* pongRefreshControl = [[BOZPongRefreshControl alloc] initWithFrame:CGRectMake(0.0f, 0.0f, scrollView.frame.size.width, 0.0f) 93 | andScrollView:scrollView 94 | andRefreshTarget:refreshTarget 95 | andRefreshAction:refreshAction]; 96 | 97 | [scrollView addSubview:pongRefreshControl]; 98 | 99 | return pongRefreshControl; 100 | } 101 | 102 | + (BOZPongRefreshControl*)findPongRefreshControlInScrollView:(UIScrollView*)scrollView 103 | { 104 | for(UIView* subview in scrollView.subviews) { 105 | if([subview isKindOfClass:[BOZPongRefreshControl class]]) { 106 | return (BOZPongRefreshControl*)subview; 107 | } 108 | } 109 | 110 | return nil; 111 | } 112 | 113 | #pragma mark - Initializing a new pong refresh control 114 | 115 | - (id)initWithFrame:(CGRect)frame 116 | andScrollView:(UIScrollView*)scrollView 117 | andRefreshTarget:(id)refreshTarget 118 | andRefreshAction:(SEL)refreshAction 119 | { 120 | self = [super initWithFrame:frame]; 121 | if (self) { 122 | self.clipsToBounds = YES; 123 | 124 | self.scrollView = scrollView; 125 | self.refreshTarget = refreshTarget; 126 | self.refreshAction = refreshAction; 127 | 128 | originalTopContentInset = scrollView.contentInset.top; 129 | 130 | [self setUpGameView]; 131 | [self setUpGamePieceIdleOrigins]; 132 | [self setUpPaddles]; 133 | [self setUpBall]; 134 | 135 | state = BOZPongRefreshControlStateIdle; 136 | 137 | self.foregroundColor = DEFAULT_FOREGROUND_COLOR; 138 | self.backgroundColor = DEFAULT_BACKGROUND_COLOR; 139 | 140 | [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 141 | [[NSNotificationCenter defaultCenter] addObserver:self 142 | selector:@selector(handleOrientationChange) 143 | name:UIDeviceOrientationDidChangeNotification 144 | object:nil]; 145 | 146 | } 147 | return self; 148 | } 149 | 150 | - (void)dealloc 151 | { 152 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; 153 | } 154 | 155 | - (void)setUpGameView 156 | { 157 | gameView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.frame.size.width, REFRESH_CONTROL_HEIGHT)]; 158 | gameView.backgroundColor = [UIColor clearColor]; 159 | [self addSubview:gameView]; 160 | } 161 | 162 | - (void)setUpGamePieceIdleOrigins 163 | { 164 | leftPaddleIdleOrigin = CGPointMake(gameView.frame.size.width * 0.25f, gameView.frame.size.height); 165 | rightPaddleIdleOrigin = CGPointMake(gameView.frame.size.width * 0.75f, gameView.frame.size.height); 166 | ballIdleOrigin = CGPointMake(gameView.frame.size.width * 0.50f, 0.0f); 167 | } 168 | 169 | - (void)setUpPaddles 170 | { 171 | leftPaddleView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 2.0f, 15.0f)]; 172 | leftPaddleView.center = leftPaddleIdleOrigin; 173 | leftPaddleView.backgroundColor = self.foregroundColor; 174 | 175 | rightPaddleView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 2.0f, 15.0f)]; 176 | rightPaddleView.center = rightPaddleIdleOrigin; 177 | rightPaddleView.backgroundColor = self.foregroundColor; 178 | 179 | [gameView addSubview:leftPaddleView]; 180 | [gameView addSubview:rightPaddleView]; 181 | } 182 | 183 | - (void)setUpBall 184 | { 185 | ballView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 3.0f, 3.0f)]; 186 | ballView.center = ballIdleOrigin; 187 | ballView.backgroundColor = self.foregroundColor; 188 | 189 | self.totalHorizontalTravelTimeForBall = DEFAULT_TOTAL_HORIZONTAL_TRAVEL_TIME_FOR_BALL; 190 | 191 | [gameView addSubview:ballView]; 192 | } 193 | 194 | #pragma mark - Handling various configuration changes 195 | 196 | - (void)setBackgroundColor:(UIColor *)backgroundColor 197 | { 198 | [super setBackgroundColor:backgroundColor]; 199 | } 200 | 201 | - (void)setForegroundColor:(UIColor*)foregroundColor 202 | { 203 | _foregroundColor = foregroundColor; 204 | 205 | leftPaddleView.backgroundColor = foregroundColor; 206 | rightPaddleView.backgroundColor = foregroundColor; 207 | ballView.backgroundColor = foregroundColor; 208 | } 209 | 210 | #pragma mark - Listening to scroll delegate events 211 | 212 | #pragma mark Actively scrolling 213 | 214 | - (void)scrollViewDidScroll 215 | { 216 | CGFloat rawOffset = -self.distanceScrolled; 217 | 218 | [self offsetGameViewBy:rawOffset]; 219 | 220 | if(state == BOZPongRefreshControlStateIdle) { 221 | CGFloat ballAndPaddlesOffset = MIN(rawOffset / 2.0f, HALF_REFRESH_CONTROL_HEIGHT); 222 | 223 | [self offsetBallAndPaddlesBy:ballAndPaddlesOffset]; 224 | [self rotatePaddlesAccordingToOffset:ballAndPaddlesOffset]; 225 | } 226 | } 227 | 228 | - (CGFloat)distanceScrolled 229 | { 230 | return (self.scrollView.contentOffset.y + self.scrollView.contentInset.top); 231 | } 232 | 233 | - (void)offsetGameViewBy:(CGFloat)offset 234 | { 235 | CGFloat offsetConsideringState = offset; 236 | if(state != BOZPongRefreshControlStateIdle) { 237 | offsetConsideringState += REFRESH_CONTROL_HEIGHT; 238 | } 239 | 240 | [self setHeightAndOffsetOfRefreshControl:offsetConsideringState]; 241 | [self stickGameViewToBottomOfRefreshControl]; 242 | } 243 | 244 | - (void)setHeightAndOffsetOfRefreshControl:(CGFloat)offset 245 | { 246 | CGRect newFrame = self.frame; 247 | newFrame.size.height = offset; 248 | newFrame.origin.y = -offset; 249 | self.frame = newFrame; 250 | } 251 | 252 | - (void)stickGameViewToBottomOfRefreshControl 253 | { 254 | CGRect newGameViewFrame = gameView.frame; 255 | newGameViewFrame.origin.y = self.frame.size.height - gameView.frame.size.height; 256 | gameView.frame = newGameViewFrame; 257 | } 258 | 259 | - (void)offsetBallAndPaddlesBy:(CGFloat)offset 260 | { 261 | ballView.center = CGPointMake(ballIdleOrigin.x, ballIdleOrigin.y + offset); 262 | leftPaddleView.center = CGPointMake(leftPaddleIdleOrigin.x, leftPaddleIdleOrigin.y - offset); 263 | rightPaddleView.center = CGPointMake(rightPaddleIdleOrigin.x, rightPaddleIdleOrigin.y - offset); 264 | } 265 | 266 | - (void)rotatePaddlesAccordingToOffset:(CGFloat)offset 267 | { 268 | CGFloat proportionOfMaxOffset = (offset / HALF_REFRESH_CONTROL_HEIGHT); 269 | CGFloat angleToRotate = M_PI * proportionOfMaxOffset; 270 | 271 | leftPaddleView.transform = CGAffineTransformMakeRotation(angleToRotate); 272 | rightPaddleView.transform = CGAffineTransformMakeRotation(-angleToRotate); 273 | } 274 | 275 | #pragma mark Letting go of the scroll view, checking for refresh trigger 276 | 277 | - (void)scrollViewDidEndDragging 278 | { 279 | if(state == BOZPongRefreshControlStateIdle) { 280 | if([self didUserScrollFarEnoughToTriggerRefresh]) { 281 | [self beginLoading]; 282 | [self notifyTargetOfRefreshTrigger]; 283 | } 284 | } 285 | } 286 | 287 | - (BOOL)didUserScrollFarEnoughToTriggerRefresh 288 | { 289 | return (-self.distanceScrolled > REFRESH_CONTROL_HEIGHT); 290 | } 291 | 292 | - (void)notifyTargetOfRefreshTrigger 293 | { 294 | #pragma clang diagnostic push 295 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 296 | 297 | if ([self.refreshTarget respondsToSelector:self.refreshAction]) 298 | [self.refreshTarget performSelector:self.refreshAction]; 299 | 300 | #pragma clang diagnostic pop 301 | } 302 | 303 | #pragma mark - Manually starting a refresh 304 | 305 | - (void)beginLoading 306 | { 307 | [self beginLoadingAnimated:YES]; 308 | } 309 | 310 | - (void)beginLoadingAnimated:(BOOL)animated 311 | { 312 | if (state != BOZPongRefreshControlStateRefreshing) { 313 | state = BOZPongRefreshControlStateRefreshing; 314 | 315 | [self scrollRefreshControlToVisibleAnimated:animated]; 316 | [self startPong]; 317 | } 318 | } 319 | 320 | - (void)scrollRefreshControlToVisibleAnimated:(BOOL)animated 321 | { 322 | CGFloat animationDuration = 0.0f; 323 | if(animated) { 324 | animationDuration = TRANSITION_ANIMATION_DURATION; 325 | } 326 | 327 | 328 | UIEdgeInsets newInsets = self.scrollView.contentInset; 329 | newInsets.top = originalTopContentInset + REFRESH_CONTROL_HEIGHT; 330 | CGPoint contentOffset = self.scrollView.contentOffset; 331 | 332 | [UIView animateWithDuration:animationDuration animations:^(void) { 333 | self.scrollView.contentInset = newInsets; 334 | self.scrollView.contentOffset = contentOffset; 335 | }]; 336 | } 337 | 338 | #pragma mark - Resetting after loading finished 339 | 340 | - (void)finishedLoading 341 | { 342 | if(state != BOZPongRefreshControlStateRefreshing) { 343 | return; 344 | } 345 | 346 | state = BOZPongRefreshControlStateResetting; 347 | 348 | [UIView animateWithDuration:TRANSITION_ANIMATION_DURATION animations:^(void) 349 | { 350 | [self resetScrollViewContentInsets]; 351 | [self setHeightAndOffsetOfRefreshControl:0.0f]; 352 | } 353 | completion:^(BOOL finished) 354 | { 355 | [self resetPaddlesAndBall]; 356 | state = BOZPongRefreshControlStateIdle; 357 | }]; 358 | } 359 | 360 | - (void)resetScrollViewContentInsets 361 | { 362 | UIEdgeInsets newInsets = self.scrollView.contentInset; 363 | newInsets.top = originalTopContentInset; 364 | self.scrollView.contentInset = newInsets; 365 | } 366 | 367 | - (void)resetPaddlesAndBall 368 | { 369 | [self removeAnimations]; 370 | 371 | leftPaddleView.center = leftPaddleIdleOrigin; 372 | rightPaddleView.center = rightPaddleIdleOrigin; 373 | ballView.center = ballIdleOrigin; 374 | } 375 | 376 | - (void)removeAnimations 377 | { 378 | [leftPaddleView.layer removeAllAnimations]; 379 | [rightPaddleView.layer removeAllAnimations]; 380 | [ballView.layer removeAllAnimations]; 381 | } 382 | 383 | #pragma mark - Playing pong 384 | 385 | #pragma mark Starting the game 386 | 387 | - (void)startPong 388 | { 389 | ballOrigin = ballView.center; 390 | leftPaddleOrigin = leftPaddleIdleOrigin.y; 391 | rightPaddleOrigin = rightPaddleIdleOrigin.y; 392 | 393 | [self pickRandomStartingBallDestination]; 394 | [self determineNextPaddleDestinations]; 395 | [self animateBallAndPaddlesToDestinations]; 396 | } 397 | 398 | - (void)pickRandomStartingBallDestination 399 | { 400 | CGFloat destinationX = [self leftPaddleContactX]; 401 | if(arc4random() % 2 == 1) { 402 | destinationX = [self rightPaddleContactX]; 403 | } 404 | CGFloat destinationY = (float)(arc4random() % (int)gameView.frame.size.height); 405 | 406 | ballDestination = CGPointMake(destinationX, destinationY); 407 | ballDirection = CGPointMake((ballDestination.x - ballOrigin.x), (ballDestination.y - ballOrigin.y)); 408 | ballDirection = [self normalizeVector:ballDirection]; 409 | } 410 | 411 | #pragma mark Playing the game 412 | 413 | #pragma mark Ball behavior 414 | 415 | - (void)determineNextBallDestination 416 | { 417 | CGFloat newBallDestinationX; 418 | CGFloat newBallDestinationY; 419 | 420 | ballDirection = [self determineReflectedDirectionOfBall]; 421 | 422 | CGFloat verticalDistanceToNextWall = [self calculateVerticalDistanceFromBallToNextWall]; 423 | CGFloat distanceToNextWall = verticalDistanceToNextWall / ballDirection.y; 424 | CGFloat horizontalDistanceToNextWall = distanceToNextWall * ballDirection.x; 425 | 426 | CGFloat horizontalDistanceToNextPaddle = [self calculateHorizontalDistanceFromBallToNextPaddle]; 427 | 428 | if(fabs(horizontalDistanceToNextPaddle) < fabs(horizontalDistanceToNextWall)) { 429 | newBallDestinationX = ballDestination.x + horizontalDistanceToNextPaddle; 430 | 431 | CGFloat verticalDistanceToNextPaddle = fabs(horizontalDistanceToNextPaddle) * ballDirection.y; 432 | newBallDestinationY = ballDestination.y + verticalDistanceToNextPaddle; 433 | } else { 434 | newBallDestinationX = ballDestination.x + horizontalDistanceToNextWall; 435 | newBallDestinationY = ballDestination.y + verticalDistanceToNextWall; 436 | } 437 | 438 | ballOrigin = ballDestination; 439 | ballDestination = CGPointMake(newBallDestinationX, newBallDestinationY); 440 | } 441 | 442 | - (CGPoint)determineReflectedDirectionOfBall 443 | { 444 | CGPoint reflectedBallDirection = ballDirection; 445 | 446 | if([self didBallHitWall]) { 447 | reflectedBallDirection = CGPointMake(ballDirection.x, -ballDirection.y); 448 | } else if([self didBallHitPaddle]) { 449 | reflectedBallDirection = CGPointMake(-ballDirection.x, ballDirection.y); 450 | } 451 | 452 | return reflectedBallDirection; 453 | } 454 | 455 | - (BOOL)didBallHitWall 456 | { 457 | return ([self isFloat:ballDestination.y equalToFloat:[self ceilingContactY]] || [self isFloat:ballDestination.y equalToFloat:[self floorContactY]]); 458 | } 459 | 460 | - (BOOL)didBallHitPaddle 461 | { 462 | return ([self isFloat:ballDestination.x equalToFloat:[self leftPaddleContactX]] || [self isFloat:ballDestination.x equalToFloat:[self rightPaddleContactX]]); 463 | } 464 | 465 | - (CGFloat)calculateVerticalDistanceFromBallToNextWall 466 | { 467 | if(ballDirection.y > 0.0f) { 468 | return [self floorContactY] - ballDestination.y; 469 | } else { 470 | return [self ceilingContactY] - ballDestination.y; 471 | } 472 | } 473 | 474 | - (CGFloat)calculateHorizontalDistanceFromBallToNextPaddle 475 | { 476 | if(ballDirection.x < 0.0f) { 477 | return [self leftPaddleContactX] - ballDestination.x; 478 | } else { 479 | return [self rightPaddleContactX] - ballDestination.x; 480 | } 481 | } 482 | 483 | #pragma mark Paddle behavior 484 | 485 | - (void)determineNextPaddleDestinations 486 | { 487 | static CGFloat lazySpeedFactor = 0.25f; 488 | static CGFloat normalSpeedFactor = 0.5f; 489 | static CGFloat holyCrapSpeedFactor = 1.0f; 490 | 491 | CGFloat leftPaddleVerticalDistanceToBallDestination = ballDestination.y - leftPaddleView.center.y; 492 | CGFloat rightPaddleVerticalDistanceToBallDestination = ballDestination.y - rightPaddleView.center.y; 493 | 494 | CGFloat leftPaddleOffset; 495 | CGFloat rightPaddleOffset; 496 | 497 | //Determining how far each paddle will mode 498 | if(ballDirection.x < 0.0f) { 499 | //Ball is going toward the left paddle 500 | 501 | if([self isBallDestinationTheLeftPaddle]) { 502 | leftPaddleOffset = (leftPaddleVerticalDistanceToBallDestination * holyCrapSpeedFactor); 503 | rightPaddleOffset = (rightPaddleVerticalDistanceToBallDestination * lazySpeedFactor); 504 | } else { 505 | //Destination is a wall 506 | leftPaddleOffset = (leftPaddleVerticalDistanceToBallDestination * normalSpeedFactor); 507 | rightPaddleOffset = -(rightPaddleVerticalDistanceToBallDestination * normalSpeedFactor); 508 | } 509 | } else { 510 | //Ball is going toward the right paddle 511 | 512 | if([self isBallDestinationTheRightPaddle]) { 513 | leftPaddleOffset = (leftPaddleVerticalDistanceToBallDestination * lazySpeedFactor); 514 | rightPaddleOffset = (rightPaddleVerticalDistanceToBallDestination * holyCrapSpeedFactor); 515 | } else { 516 | //Destination is a wall 517 | leftPaddleOffset = -(leftPaddleVerticalDistanceToBallDestination * normalSpeedFactor); 518 | rightPaddleOffset = (rightPaddleVerticalDistanceToBallDestination * normalSpeedFactor); 519 | } 520 | } 521 | leftPaddleOrigin = leftPaddleDestination; 522 | rightPaddleOrigin = rightPaddleDestination; 523 | leftPaddleDestination = leftPaddleView.center.y + leftPaddleOffset; 524 | rightPaddleDestination = rightPaddleView.center.y + rightPaddleOffset; 525 | 526 | [self capPaddleDestinationsToWalls]; 527 | } 528 | 529 | - (BOOL)isBallDestinationTheLeftPaddle 530 | { 531 | return ([self isFloat:ballDestination.x equalToFloat:[self leftPaddleContactX]]); 532 | } 533 | 534 | - (BOOL)isBallDestinationTheRightPaddle 535 | { 536 | return ([self isFloat:ballDestination.x equalToFloat:[self rightPaddleContactX]]); 537 | } 538 | 539 | - (void)capPaddleDestinationsToWalls 540 | { 541 | if(leftPaddleDestination < [self ceilingLeftPaddleContactY]) { 542 | leftPaddleDestination = [self ceilingLeftPaddleContactY]; 543 | } else if(leftPaddleDestination > [self floorLeftPaddleContactY]) { 544 | leftPaddleDestination = [self floorLeftPaddleContactY]; 545 | } 546 | 547 | if(rightPaddleDestination < [self ceilingRightPaddleContactY]) { 548 | rightPaddleDestination = [self ceilingRightPaddleContactY]; 549 | } else if(rightPaddleDestination > [self floorRightPaddleContactY]) { 550 | rightPaddleDestination = [self floorRightPaddleContactY]; 551 | } 552 | } 553 | 554 | #pragma mark Actually animating the balls and paddles to where they need to go 555 | 556 | - (void)animateBallAndPaddlesToDestinations 557 | { 558 | currentAnimationStartTime = [NSDate date]; 559 | currentAnimationDuration = [self calculateAnimationDuration]; 560 | 561 | [UIView animateWithDuration:currentAnimationDuration delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^(void) 562 | { 563 | ballView.center = ballDestination; 564 | leftPaddleView.center = CGPointMake(leftPaddleView.center.x, leftPaddleDestination); 565 | rightPaddleView.center = CGPointMake(rightPaddleView.center.x, rightPaddleDestination); 566 | } 567 | completion:^(BOOL finished) 568 | { 569 | if(finished) { 570 | [self determineNextBallDestination]; 571 | [self determineNextPaddleDestinations]; 572 | [self animateBallAndPaddlesToDestinations]; 573 | } 574 | }]; 575 | } 576 | 577 | - (CGFloat)calculateAnimationDuration 578 | { 579 | CGFloat endToEndDistance = [self rightPaddleContactX] - [self leftPaddleContactX]; 580 | CGFloat proportionOfHorizontalDistanceLeftForBallToTravel = fabsf((ballDestination.x - ballOrigin.x) / endToEndDistance); 581 | return (self.totalHorizontalTravelTimeForBall * proportionOfHorizontalDistanceLeftForBallToTravel); 582 | } 583 | 584 | #pragma mark Helper functions for collision detection 585 | 586 | #pragma mark Ball collisions 587 | 588 | - (CGFloat)leftPaddleContactX 589 | { 590 | return leftPaddleView.center.x + (ballView.frame.size.width / 2.0f); 591 | } 592 | 593 | - (CGFloat)rightPaddleContactX 594 | { 595 | return rightPaddleView.center.x - (ballView.frame.size.width / 2.0f); 596 | } 597 | 598 | - (CGFloat)ceilingContactY 599 | { 600 | return (ballView.frame.size.height / 2.0f); 601 | } 602 | 603 | - (CGFloat)floorContactY 604 | { 605 | return gameView.frame.size.height - (ballView.frame.size.height / 2.0f); 606 | } 607 | 608 | #pragma mark Paddle collisions 609 | 610 | - (CGFloat)ceilingLeftPaddleContactY 611 | { 612 | return (leftPaddleView.frame.size.height / 2.0f); 613 | } 614 | 615 | - (CGFloat)floorLeftPaddleContactY 616 | { 617 | return gameView.frame.size.height - (leftPaddleView.frame.size.height / 2.0f); 618 | } 619 | 620 | - (CGFloat)ceilingRightPaddleContactY 621 | { 622 | return (rightPaddleView.frame.size.height / 2.0f); 623 | } 624 | 625 | - (CGFloat)floorRightPaddleContactY 626 | { 627 | return gameView.frame.size.height - (rightPaddleView.frame.size.height / 2.0f); 628 | } 629 | 630 | #pragma mark - Handling orientation changes 631 | 632 | - (void)handleOrientationChange { 633 | self.frame = CGRectMake(0.0f, 0.0f, self.scrollView.frame.size.width, 0.0f); 634 | CGFloat gameViewWidthBeforeOrientationChange = gameView.frame.size.width; 635 | gameView.frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, REFRESH_CONTROL_HEIGHT); 636 | 637 | originalTopContentInset = self.scrollView.contentInset.top; 638 | 639 | [self setUpGamePieceIdleOrigins]; 640 | 641 | if(state == BOZPongRefreshControlStateRefreshing) { 642 | originalTopContentInset -= REFRESH_CONTROL_HEIGHT; 643 | [self setHeightAndOffsetOfRefreshControl:REFRESH_CONTROL_HEIGHT]; 644 | 645 | [self removeAnimations]; 646 | CGFloat horizontalScaleFactor = gameView.frame.size.width / gameViewWidthBeforeOrientationChange; 647 | [self setGamePiecePositionsForAnimationStop:horizontalScaleFactor]; 648 | 649 | [self animateBallAndPaddlesToDestinations]; 650 | } else { 651 | [self setGamePiecePositionsToIdle]; 652 | } 653 | } 654 | 655 | - (void)setGamePiecePositionsForAnimationStop:(CGFloat)horizontalScaleFactor 656 | { 657 | //Place the game pieces as though the animation got cut off 658 | 659 | CGFloat timeSinceCurrentAnimationStarted = -[currentAnimationStartTime timeIntervalSinceNow]; 660 | CGFloat proportionOfCurrentAnimationCompleted = timeSinceCurrentAnimationStarted / currentAnimationDuration; 661 | 662 | CGPoint totalBallDisplacementForCurrentAnimation = CGPointMake(ballDestination.x - ballOrigin.x, ballDestination.y - ballOrigin.y); 663 | CGFloat totalLeftPaddleDisplacementForCurrentAnimation = leftPaddleDestination - leftPaddleOrigin; 664 | CGFloat totalRightPaddleDisplacementForCurrentAnimation = rightPaddleDestination - rightPaddleOrigin; 665 | 666 | ballView.center = CGPointMake(ballOrigin.x + (totalBallDisplacementForCurrentAnimation.x * proportionOfCurrentAnimationCompleted), 667 | ballOrigin.y + (totalBallDisplacementForCurrentAnimation.y * proportionOfCurrentAnimationCompleted)); 668 | leftPaddleView.center = CGPointMake(leftPaddleView.center.x, 669 | leftPaddleOrigin + (totalLeftPaddleDisplacementForCurrentAnimation * proportionOfCurrentAnimationCompleted)); 670 | rightPaddleView.center = CGPointMake(rightPaddleView.center.x, 671 | rightPaddleOrigin + (totalRightPaddleDisplacementForCurrentAnimation * proportionOfCurrentAnimationCompleted)); 672 | 673 | ballOrigin = ballView.center; 674 | leftPaddleOrigin = leftPaddleView.center.y; 675 | rightPaddleOrigin = rightPaddleView.center.y; 676 | 677 | //Now scale everything for the change in horizontal distance 678 | 679 | leftPaddleView.center = CGPointMake(leftPaddleView.center.x * horizontalScaleFactor, leftPaddleView.center.y); 680 | rightPaddleView.center = CGPointMake(rightPaddleView.center.x * horizontalScaleFactor, rightPaddleView.center.y); 681 | 682 | ballView.center = CGPointMake(ballView.center.x * horizontalScaleFactor, ballView.center.y); 683 | ballOrigin = CGPointMake(ballOrigin.x * horizontalScaleFactor, ballOrigin.y); 684 | ballDestination = CGPointMake(ballDestination.x * horizontalScaleFactor, ballDestination.y); 685 | ballDirection = [self normalizeVector:CGPointMake(ballDirection.x * horizontalScaleFactor, ballDirection.y)]; 686 | } 687 | 688 | - (void)setGamePiecePositionsToIdle 689 | { 690 | leftPaddleView.center = leftPaddleIdleOrigin; 691 | rightPaddleView.center = rightPaddleIdleOrigin; 692 | ballView.center = ballIdleOrigin; 693 | } 694 | 695 | #pragma mark - Etc, some basic math functions 696 | 697 | - (CGPoint)normalizeVector:(CGPoint)vector 698 | { 699 | CGFloat magnitude = sqrtf(vector.x * vector.x + vector.y * vector.y); 700 | return CGPointMake(vector.x / magnitude, vector.y / magnitude); 701 | } 702 | 703 | - (BOOL)isFloat:(CGFloat)float1 equalToFloat:(CGFloat)float2 704 | { 705 | static CGFloat ellipsis = 0.01f; 706 | 707 | return (fabsf(float1 - float2) < ellipsis); 708 | } 709 | 710 | @end 711 | -------------------------------------------------------------------------------- /PongRefreshControlDemo/PongRefreshControlDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FBB15602185260B90072DCEC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB15601185260B90072DCEC /* Foundation.framework */; }; 11 | FBB15604185260B90072DCEC /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB15603185260B90072DCEC /* CoreGraphics.framework */; }; 12 | FBB15606185260B90072DCEC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB15605185260B90072DCEC /* UIKit.framework */; }; 13 | FBB1560C185260B90072DCEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FBB1560A185260B90072DCEC /* InfoPlist.strings */; }; 14 | FBB1560E185260B90072DCEC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB1560D185260B90072DCEC /* main.m */; }; 15 | FBB15612185260B90072DCEC /* BOZAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB15611185260B90072DCEC /* BOZAppDelegate.m */; }; 16 | FBB15614185260B90072DCEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FBB15613185260B90072DCEC /* Images.xcassets */; }; 17 | FBB1561B185260B90072DCEC /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB1561A185260B90072DCEC /* XCTest.framework */; }; 18 | FBB1561C185260B90072DCEC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB15601185260B90072DCEC /* Foundation.framework */; }; 19 | FBB1561D185260B90072DCEC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB15605185260B90072DCEC /* UIKit.framework */; }; 20 | FBB15625185260B90072DCEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FBB15623185260B90072DCEC /* InfoPlist.strings */; }; 21 | FBB15627185260B90072DCEC /* PongRefreshControlDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB15626185260B90072DCEC /* PongRefreshControlDemoTests.m */; }; 22 | FBB15633185262A80072DCEC /* BOZPongRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB15632185262A80072DCEC /* BOZPongRefreshControl.m */; }; 23 | FBB15634185262A80072DCEC /* BOZPongRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB15632185262A80072DCEC /* BOZPongRefreshControl.m */; }; 24 | FBB1563F1852638D0072DCEC /* BOZDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB1563D1852638D0072DCEC /* BOZDemoViewController.m */; }; 25 | FBB156401852638D0072DCEC /* BOZDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB1563D1852638D0072DCEC /* BOZDemoViewController.m */; }; 26 | FBB156441852C4EC0072DCEC /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FBB156431852C4EC0072DCEC /* Storyboard.storyboard */; }; 27 | FBB156451852C4EC0072DCEC /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FBB156431852C4EC0072DCEC /* Storyboard.storyboard */; }; 28 | FBB1564B1852D76C0072DCEC /* BOZNoNavBarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB156491852D76C0072DCEC /* BOZNoNavBarViewController.m */; }; 29 | FBB1564C1852D76C0072DCEC /* BOZNoNavBarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB156491852D76C0072DCEC /* BOZNoNavBarViewController.m */; }; 30 | FBB156521852F1280072DCEC /* BOZRegularViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB156511852F1280072DCEC /* BOZRegularViewController.m */; }; 31 | FBB156531852F1280072DCEC /* BOZRegularViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB156511852F1280072DCEC /* BOZRegularViewController.m */; }; 32 | FBB156571852F9BF0072DCEC /* BOZScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB156561852F9BF0072DCEC /* BOZScrollViewController.m */; }; 33 | FBB156581852F9BF0072DCEC /* BOZScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB156561852F9BF0072DCEC /* BOZScrollViewController.m */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | FBB1561E185260B90072DCEC /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = FBB155F6185260B90072DCEC /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = FBB155FD185260B90072DCEC; 42 | remoteInfo = PongRefreshControlDemo; 43 | }; 44 | /* End PBXContainerItemProxy section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | FBB155FE185260B90072DCEC /* PongRefreshControlDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PongRefreshControlDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | FBB15601185260B90072DCEC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 49 | FBB15603185260B90072DCEC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 50 | FBB15605185260B90072DCEC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 51 | FBB15609185260B90072DCEC /* PongRefreshControlDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PongRefreshControlDemo-Info.plist"; sourceTree = ""; }; 52 | FBB1560B185260B90072DCEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 53 | FBB1560D185260B90072DCEC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | FBB1560F185260B90072DCEC /* PongRefreshControlDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PongRefreshControlDemo-Prefix.pch"; sourceTree = ""; }; 55 | FBB15610185260B90072DCEC /* BOZAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BOZAppDelegate.h; sourceTree = ""; }; 56 | FBB15611185260B90072DCEC /* BOZAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BOZAppDelegate.m; sourceTree = ""; }; 57 | FBB15613185260B90072DCEC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 58 | FBB15619185260B90072DCEC /* PongRefreshControlDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PongRefreshControlDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | FBB1561A185260B90072DCEC /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 60 | FBB15622185260B90072DCEC /* PongRefreshControlDemoTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PongRefreshControlDemoTests-Info.plist"; sourceTree = ""; }; 61 | FBB15624185260B90072DCEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 62 | FBB15626185260B90072DCEC /* PongRefreshControlDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PongRefreshControlDemoTests.m; sourceTree = ""; }; 63 | FBB15631185262A80072DCEC /* BOZPongRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BOZPongRefreshControl.h; path = ../BOZPongRefreshControl/BOZPongRefreshControl.h; sourceTree = ""; }; 64 | FBB15632185262A80072DCEC /* BOZPongRefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BOZPongRefreshControl.m; path = ../BOZPongRefreshControl/BOZPongRefreshControl.m; sourceTree = ""; }; 65 | FBB1563C1852638D0072DCEC /* BOZDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BOZDemoViewController.h; sourceTree = ""; }; 66 | FBB1563D1852638D0072DCEC /* BOZDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BOZDemoViewController.m; sourceTree = ""; }; 67 | FBB156431852C4EC0072DCEC /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; 68 | FBB156481852D76C0072DCEC /* BOZNoNavBarViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BOZNoNavBarViewController.h; sourceTree = ""; }; 69 | FBB156491852D76C0072DCEC /* BOZNoNavBarViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BOZNoNavBarViewController.m; sourceTree = ""; }; 70 | FBB156501852F1280072DCEC /* BOZRegularViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BOZRegularViewController.h; sourceTree = ""; }; 71 | FBB156511852F1280072DCEC /* BOZRegularViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BOZRegularViewController.m; sourceTree = ""; }; 72 | FBB156551852F9BF0072DCEC /* BOZScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BOZScrollViewController.h; sourceTree = ""; }; 73 | FBB156561852F9BF0072DCEC /* BOZScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BOZScrollViewController.m; sourceTree = ""; }; 74 | /* End PBXFileReference section */ 75 | 76 | /* Begin PBXFrameworksBuildPhase section */ 77 | FBB155FB185260B90072DCEC /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | FBB15604185260B90072DCEC /* CoreGraphics.framework in Frameworks */, 82 | FBB15606185260B90072DCEC /* UIKit.framework in Frameworks */, 83 | FBB15602185260B90072DCEC /* Foundation.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | FBB15616185260B90072DCEC /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | FBB1561B185260B90072DCEC /* XCTest.framework in Frameworks */, 92 | FBB1561D185260B90072DCEC /* UIKit.framework in Frameworks */, 93 | FBB1561C185260B90072DCEC /* Foundation.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | FBB155F5185260B90072DCEC = { 101 | isa = PBXGroup; 102 | children = ( 103 | FBB15630185262900072DCEC /* BOZPongRefreshControl */, 104 | FBB15607185260B90072DCEC /* PongRefreshControlDemo */, 105 | FBB15620185260B90072DCEC /* PongRefreshControlDemoTests */, 106 | FBB15600185260B90072DCEC /* Frameworks */, 107 | FBB155FF185260B90072DCEC /* Products */, 108 | ); 109 | sourceTree = ""; 110 | }; 111 | FBB155FF185260B90072DCEC /* Products */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | FBB155FE185260B90072DCEC /* PongRefreshControlDemo.app */, 115 | FBB15619185260B90072DCEC /* PongRefreshControlDemoTests.xctest */, 116 | ); 117 | name = Products; 118 | sourceTree = ""; 119 | }; 120 | FBB15600185260B90072DCEC /* Frameworks */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | FBB15601185260B90072DCEC /* Foundation.framework */, 124 | FBB15603185260B90072DCEC /* CoreGraphics.framework */, 125 | FBB15605185260B90072DCEC /* UIKit.framework */, 126 | FBB1561A185260B90072DCEC /* XCTest.framework */, 127 | ); 128 | name = Frameworks; 129 | sourceTree = ""; 130 | }; 131 | FBB15607185260B90072DCEC /* PongRefreshControlDemo */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | FBB15610185260B90072DCEC /* BOZAppDelegate.h */, 135 | FBB15611185260B90072DCEC /* BOZAppDelegate.m */, 136 | FBB156431852C4EC0072DCEC /* Storyboard.storyboard */, 137 | FBB156461852D73A0072DCEC /* Demo View Controller */, 138 | FBB156541852F9AE0072DCEC /* Scroll View Controller */, 139 | FBB1564F1852F1110072DCEC /* Regular View Controller */, 140 | FBB156471852D7500072DCEC /* No Nav Bar View Controller */, 141 | FBB15613185260B90072DCEC /* Images.xcassets */, 142 | FBB15608185260B90072DCEC /* Supporting Files */, 143 | ); 144 | path = PongRefreshControlDemo; 145 | sourceTree = ""; 146 | }; 147 | FBB15608185260B90072DCEC /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | FBB15609185260B90072DCEC /* PongRefreshControlDemo-Info.plist */, 151 | FBB1560A185260B90072DCEC /* InfoPlist.strings */, 152 | FBB1560D185260B90072DCEC /* main.m */, 153 | FBB1560F185260B90072DCEC /* PongRefreshControlDemo-Prefix.pch */, 154 | ); 155 | name = "Supporting Files"; 156 | sourceTree = ""; 157 | }; 158 | FBB15620185260B90072DCEC /* PongRefreshControlDemoTests */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | FBB15626185260B90072DCEC /* PongRefreshControlDemoTests.m */, 162 | FBB15621185260B90072DCEC /* Supporting Files */, 163 | ); 164 | path = PongRefreshControlDemoTests; 165 | sourceTree = ""; 166 | }; 167 | FBB15621185260B90072DCEC /* Supporting Files */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | FBB15622185260B90072DCEC /* PongRefreshControlDemoTests-Info.plist */, 171 | FBB15623185260B90072DCEC /* InfoPlist.strings */, 172 | ); 173 | name = "Supporting Files"; 174 | sourceTree = ""; 175 | }; 176 | FBB15630185262900072DCEC /* BOZPongRefreshControl */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | FBB15631185262A80072DCEC /* BOZPongRefreshControl.h */, 180 | FBB15632185262A80072DCEC /* BOZPongRefreshControl.m */, 181 | ); 182 | name = BOZPongRefreshControl; 183 | sourceTree = ""; 184 | }; 185 | FBB156461852D73A0072DCEC /* Demo View Controller */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | FBB1563C1852638D0072DCEC /* BOZDemoViewController.h */, 189 | FBB1563D1852638D0072DCEC /* BOZDemoViewController.m */, 190 | ); 191 | name = "Demo View Controller"; 192 | sourceTree = ""; 193 | }; 194 | FBB156471852D7500072DCEC /* No Nav Bar View Controller */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | FBB156481852D76C0072DCEC /* BOZNoNavBarViewController.h */, 198 | FBB156491852D76C0072DCEC /* BOZNoNavBarViewController.m */, 199 | ); 200 | name = "No Nav Bar View Controller"; 201 | sourceTree = ""; 202 | }; 203 | FBB1564F1852F1110072DCEC /* Regular View Controller */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | FBB156501852F1280072DCEC /* BOZRegularViewController.h */, 207 | FBB156511852F1280072DCEC /* BOZRegularViewController.m */, 208 | ); 209 | name = "Regular View Controller"; 210 | sourceTree = ""; 211 | }; 212 | FBB156541852F9AE0072DCEC /* Scroll View Controller */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | FBB156551852F9BF0072DCEC /* BOZScrollViewController.h */, 216 | FBB156561852F9BF0072DCEC /* BOZScrollViewController.m */, 217 | ); 218 | name = "Scroll View Controller"; 219 | sourceTree = ""; 220 | }; 221 | /* End PBXGroup section */ 222 | 223 | /* Begin PBXNativeTarget section */ 224 | FBB155FD185260B90072DCEC /* PongRefreshControlDemo */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = FBB1562A185260B90072DCEC /* Build configuration list for PBXNativeTarget "PongRefreshControlDemo" */; 227 | buildPhases = ( 228 | FBB155FA185260B90072DCEC /* Sources */, 229 | FBB155FB185260B90072DCEC /* Frameworks */, 230 | FBB155FC185260B90072DCEC /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | ); 236 | name = PongRefreshControlDemo; 237 | productName = PongRefreshControlDemo; 238 | productReference = FBB155FE185260B90072DCEC /* PongRefreshControlDemo.app */; 239 | productType = "com.apple.product-type.application"; 240 | }; 241 | FBB15618185260B90072DCEC /* PongRefreshControlDemoTests */ = { 242 | isa = PBXNativeTarget; 243 | buildConfigurationList = FBB1562D185260B90072DCEC /* Build configuration list for PBXNativeTarget "PongRefreshControlDemoTests" */; 244 | buildPhases = ( 245 | FBB15615185260B90072DCEC /* Sources */, 246 | FBB15616185260B90072DCEC /* Frameworks */, 247 | FBB15617185260B90072DCEC /* Resources */, 248 | ); 249 | buildRules = ( 250 | ); 251 | dependencies = ( 252 | FBB1561F185260B90072DCEC /* PBXTargetDependency */, 253 | ); 254 | name = PongRefreshControlDemoTests; 255 | productName = PongRefreshControlDemoTests; 256 | productReference = FBB15619185260B90072DCEC /* PongRefreshControlDemoTests.xctest */; 257 | productType = "com.apple.product-type.bundle.unit-test"; 258 | }; 259 | /* End PBXNativeTarget section */ 260 | 261 | /* Begin PBXProject section */ 262 | FBB155F6185260B90072DCEC /* Project object */ = { 263 | isa = PBXProject; 264 | attributes = { 265 | CLASSPREFIX = BOZ; 266 | LastUpgradeCheck = 0600; 267 | ORGANIZATIONNAME = "Ben Oztalay"; 268 | TargetAttributes = { 269 | FBB15618185260B90072DCEC = { 270 | TestTargetID = FBB155FD185260B90072DCEC; 271 | }; 272 | }; 273 | }; 274 | buildConfigurationList = FBB155F9185260B90072DCEC /* Build configuration list for PBXProject "PongRefreshControlDemo" */; 275 | compatibilityVersion = "Xcode 3.2"; 276 | developmentRegion = English; 277 | hasScannedForEncodings = 0; 278 | knownRegions = ( 279 | en, 280 | ); 281 | mainGroup = FBB155F5185260B90072DCEC; 282 | productRefGroup = FBB155FF185260B90072DCEC /* Products */; 283 | projectDirPath = ""; 284 | projectRoot = ""; 285 | targets = ( 286 | FBB155FD185260B90072DCEC /* PongRefreshControlDemo */, 287 | FBB15618185260B90072DCEC /* PongRefreshControlDemoTests */, 288 | ); 289 | }; 290 | /* End PBXProject section */ 291 | 292 | /* Begin PBXResourcesBuildPhase section */ 293 | FBB155FC185260B90072DCEC /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | FBB1560C185260B90072DCEC /* InfoPlist.strings in Resources */, 298 | FBB15614185260B90072DCEC /* Images.xcassets in Resources */, 299 | FBB156441852C4EC0072DCEC /* Storyboard.storyboard in Resources */, 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | FBB15617185260B90072DCEC /* Resources */ = { 304 | isa = PBXResourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | FBB15625185260B90072DCEC /* InfoPlist.strings in Resources */, 308 | FBB156451852C4EC0072DCEC /* Storyboard.storyboard in Resources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXResourcesBuildPhase section */ 313 | 314 | /* Begin PBXSourcesBuildPhase section */ 315 | FBB155FA185260B90072DCEC /* Sources */ = { 316 | isa = PBXSourcesBuildPhase; 317 | buildActionMask = 2147483647; 318 | files = ( 319 | FBB15612185260B90072DCEC /* BOZAppDelegate.m in Sources */, 320 | FBB1563F1852638D0072DCEC /* BOZDemoViewController.m in Sources */, 321 | FBB1564B1852D76C0072DCEC /* BOZNoNavBarViewController.m in Sources */, 322 | FBB156571852F9BF0072DCEC /* BOZScrollViewController.m in Sources */, 323 | FBB156521852F1280072DCEC /* BOZRegularViewController.m in Sources */, 324 | FBB1560E185260B90072DCEC /* main.m in Sources */, 325 | FBB15633185262A80072DCEC /* BOZPongRefreshControl.m in Sources */, 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | FBB15615185260B90072DCEC /* Sources */ = { 330 | isa = PBXSourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | FBB15634185262A80072DCEC /* BOZPongRefreshControl.m in Sources */, 334 | FBB156401852638D0072DCEC /* BOZDemoViewController.m in Sources */, 335 | FBB15627185260B90072DCEC /* PongRefreshControlDemoTests.m in Sources */, 336 | FBB156531852F1280072DCEC /* BOZRegularViewController.m in Sources */, 337 | FBB1564C1852D76C0072DCEC /* BOZNoNavBarViewController.m in Sources */, 338 | FBB156581852F9BF0072DCEC /* BOZScrollViewController.m in Sources */, 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | /* End PBXSourcesBuildPhase section */ 343 | 344 | /* Begin PBXTargetDependency section */ 345 | FBB1561F185260B90072DCEC /* PBXTargetDependency */ = { 346 | isa = PBXTargetDependency; 347 | target = FBB155FD185260B90072DCEC /* PongRefreshControlDemo */; 348 | targetProxy = FBB1561E185260B90072DCEC /* PBXContainerItemProxy */; 349 | }; 350 | /* End PBXTargetDependency section */ 351 | 352 | /* Begin PBXVariantGroup section */ 353 | FBB1560A185260B90072DCEC /* InfoPlist.strings */ = { 354 | isa = PBXVariantGroup; 355 | children = ( 356 | FBB1560B185260B90072DCEC /* en */, 357 | ); 358 | name = InfoPlist.strings; 359 | sourceTree = ""; 360 | }; 361 | FBB15623185260B90072DCEC /* InfoPlist.strings */ = { 362 | isa = PBXVariantGroup; 363 | children = ( 364 | FBB15624185260B90072DCEC /* en */, 365 | ); 366 | name = InfoPlist.strings; 367 | sourceTree = ""; 368 | }; 369 | /* End PBXVariantGroup section */ 370 | 371 | /* Begin XCBuildConfiguration section */ 372 | FBB15628185260B90072DCEC /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ALWAYS_SEARCH_USER_PATHS = NO; 376 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 377 | CLANG_CXX_LIBRARY = "libc++"; 378 | CLANG_ENABLE_MODULES = YES; 379 | CLANG_ENABLE_OBJC_ARC = YES; 380 | CLANG_WARN_BOOL_CONVERSION = YES; 381 | CLANG_WARN_CONSTANT_CONVERSION = YES; 382 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 383 | CLANG_WARN_EMPTY_BODY = YES; 384 | CLANG_WARN_ENUM_CONVERSION = YES; 385 | CLANG_WARN_INT_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 389 | COPY_PHASE_STRIP = NO; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_DYNAMIC_NO_PIC = NO; 392 | GCC_OPTIMIZATION_LEVEL = 0; 393 | GCC_PREPROCESSOR_DEFINITIONS = ( 394 | "DEBUG=1", 395 | "$(inherited)", 396 | ); 397 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 400 | GCC_WARN_UNDECLARED_SELECTOR = YES; 401 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 402 | GCC_WARN_UNUSED_FUNCTION = YES; 403 | GCC_WARN_UNUSED_VARIABLE = YES; 404 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 405 | ONLY_ACTIVE_ARCH = YES; 406 | SDKROOT = iphoneos; 407 | }; 408 | name = Debug; 409 | }; 410 | FBB15629185260B90072DCEC /* Release */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | ALWAYS_SEARCH_USER_PATHS = NO; 414 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 415 | CLANG_CXX_LIBRARY = "libc++"; 416 | CLANG_ENABLE_MODULES = YES; 417 | CLANG_ENABLE_OBJC_ARC = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INT_CONVERSION = YES; 424 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 425 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 426 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 427 | COPY_PHASE_STRIP = YES; 428 | ENABLE_NS_ASSERTIONS = NO; 429 | GCC_C_LANGUAGE_STANDARD = gnu99; 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 437 | SDKROOT = iphoneos; 438 | VALIDATE_PRODUCT = YES; 439 | }; 440 | name = Release; 441 | }; 442 | FBB1562B185260B90072DCEC /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 446 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 447 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 448 | GCC_PREFIX_HEADER = "PongRefreshControlDemo/PongRefreshControlDemo-Prefix.pch"; 449 | INFOPLIST_FILE = "PongRefreshControlDemo/PongRefreshControlDemo-Info.plist"; 450 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 451 | PRODUCT_NAME = "$(TARGET_NAME)"; 452 | WRAPPER_EXTENSION = app; 453 | }; 454 | name = Debug; 455 | }; 456 | FBB1562C185260B90072DCEC /* Release */ = { 457 | isa = XCBuildConfiguration; 458 | buildSettings = { 459 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 460 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 461 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 462 | GCC_PREFIX_HEADER = "PongRefreshControlDemo/PongRefreshControlDemo-Prefix.pch"; 463 | INFOPLIST_FILE = "PongRefreshControlDemo/PongRefreshControlDemo-Info.plist"; 464 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | WRAPPER_EXTENSION = app; 467 | }; 468 | name = Release; 469 | }; 470 | FBB1562E185260B90072DCEC /* Debug */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PongRefreshControlDemo.app/PongRefreshControlDemo"; 474 | FRAMEWORK_SEARCH_PATHS = ( 475 | "$(SDKROOT)/Developer/Library/Frameworks", 476 | "$(inherited)", 477 | "$(DEVELOPER_FRAMEWORKS_DIR)", 478 | ); 479 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 480 | GCC_PREFIX_HEADER = "PongRefreshControlDemo/PongRefreshControlDemo-Prefix.pch"; 481 | GCC_PREPROCESSOR_DEFINITIONS = ( 482 | "DEBUG=1", 483 | "$(inherited)", 484 | ); 485 | INFOPLIST_FILE = "PongRefreshControlDemoTests/PongRefreshControlDemoTests-Info.plist"; 486 | PRODUCT_NAME = "$(TARGET_NAME)"; 487 | TEST_HOST = "$(BUNDLE_LOADER)"; 488 | WRAPPER_EXTENSION = xctest; 489 | }; 490 | name = Debug; 491 | }; 492 | FBB1562F185260B90072DCEC /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PongRefreshControlDemo.app/PongRefreshControlDemo"; 496 | FRAMEWORK_SEARCH_PATHS = ( 497 | "$(SDKROOT)/Developer/Library/Frameworks", 498 | "$(inherited)", 499 | "$(DEVELOPER_FRAMEWORKS_DIR)", 500 | ); 501 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 502 | GCC_PREFIX_HEADER = "PongRefreshControlDemo/PongRefreshControlDemo-Prefix.pch"; 503 | INFOPLIST_FILE = "PongRefreshControlDemoTests/PongRefreshControlDemoTests-Info.plist"; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | TEST_HOST = "$(BUNDLE_LOADER)"; 506 | WRAPPER_EXTENSION = xctest; 507 | }; 508 | name = Release; 509 | }; 510 | /* End XCBuildConfiguration section */ 511 | 512 | /* Begin XCConfigurationList section */ 513 | FBB155F9185260B90072DCEC /* Build configuration list for PBXProject "PongRefreshControlDemo" */ = { 514 | isa = XCConfigurationList; 515 | buildConfigurations = ( 516 | FBB15628185260B90072DCEC /* Debug */, 517 | FBB15629185260B90072DCEC /* Release */, 518 | ); 519 | defaultConfigurationIsVisible = 0; 520 | defaultConfigurationName = Release; 521 | }; 522 | FBB1562A185260B90072DCEC /* Build configuration list for PBXNativeTarget "PongRefreshControlDemo" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | FBB1562B185260B90072DCEC /* Debug */, 526 | FBB1562C185260B90072DCEC /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | FBB1562D185260B90072DCEC /* Build configuration list for PBXNativeTarget "PongRefreshControlDemoTests" */ = { 532 | isa = XCConfigurationList; 533 | buildConfigurations = ( 534 | FBB1562E185260B90072DCEC /* Debug */, 535 | FBB1562F185260B90072DCEC /* Release */, 536 | ); 537 | defaultConfigurationIsVisible = 0; 538 | defaultConfigurationName = Release; 539 | }; 540 | /* End XCConfigurationList section */ 541 | }; 542 | rootObject = FBB155F6185260B90072DCEC /* Project object */; 543 | } 544 | --------------------------------------------------------------------------------