├── img
├── 1.gif
├── 1.png
├── 2.gif
├── 2.png
├── 3.gif
├── 3.png
├── 4.gif
└── 4.png
├── .slather.yml
├── Gemfile
├── Example
├── GliderSample.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── GliderSample.xcscheme
│ └── project.pbxproj
├── GliderSample
│ ├── Helpers
│ │ ├── RDButton.h
│ │ ├── RDGradientView.h
│ │ ├── RDGradientView.m
│ │ └── RDButton.m
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── main.m
│ ├── ContentViewController.h
│ ├── ContentViewController.m
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.m
│ ├── ViewController.m
│ └── ContentViewController.xib
└── GliderSampleTests
│ ├── Info.plist
│ ├── RDScrollViewTests.m
│ └── RDGliderViewControllerTests.m
├── .travis.yml
├── RDGliderViewController
├── RDGliderContentViewController.h
├── RDGliderContentViewController.m
├── RDScrollView.h
├── RDGliderViewController.h
├── RDGliderViewController.m
└── RDScrollView.m
├── LICENSE
├── RDGliderViewController.podspec
├── CHANGELOG.md
└── README.md
/img/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/1.gif
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/1.png
--------------------------------------------------------------------------------
/img/2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/2.gif
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/2.png
--------------------------------------------------------------------------------
/img/3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/3.gif
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/3.png
--------------------------------------------------------------------------------
/img/4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/4.gif
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gelemias/RDGliderViewController/HEAD/img/4.png
--------------------------------------------------------------------------------
/.slather.yml:
--------------------------------------------------------------------------------
1 | # .slather.yml
2 |
3 | coverage_service: coveralls
4 | xcodeproj: Example/GliderSample.xcodeproj
5 | scheme: GliderSample
6 | ignore:
7 | - ../*
8 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | source "https://rubygems.org"
3 | ruby '~> 2.3.0'
4 |
5 | gem 'coveralls', require: false
6 | gem 'slather', require: false
7 |
--------------------------------------------------------------------------------
/Example/GliderSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/GliderSample/Helpers/RDButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // GAButton.h
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RDButton : UIButton
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Example/GliderSample/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/Example/GliderSample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/Example/GliderSample/Helpers/RDGradientView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDGradientView.h
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RDGradientView : UIView
12 |
13 | @property (nonatomic, strong, readonly) CAGradientLayer *layer;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Example/GliderSample/Helpers/RDGradientView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDGradientView.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
7 | //
8 |
9 | #import "RDGradientView.h"
10 |
11 | @implementation RDGradientView
12 |
13 | @dynamic layer;
14 |
15 | + (Class)layerClass {
16 | return [CAGradientLayer class];
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Example/GliderSample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/GliderSample/ContentViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ContentViewController.h
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RDGliderContentViewController.h"
11 |
12 | @interface ContentViewController : RDGliderContentViewController
13 |
14 | - (void)setIndex:(NSUInteger)index ofMax:(NSUInteger)max;
15 | - (void)setOffset:(NSString *)offset;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode8.3
3 |
4 | before_install:
5 | rvm install 2.3.0
6 |
7 | script:
8 | set -o pipefail; xcodebuild -project Example/GliderSample.xcodeproj -scheme GliderSample clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO;
9 | set -o pipefail; xcodebuild test -project Example/GliderSample.xcodeproj -scheme GliderSample -destination "platform=iOS Simulator,name=iPhone 6,OS=latest";
10 | after_success:
11 | slather
12 |
13 | notifications:
14 | email: false
15 |
--------------------------------------------------------------------------------
/RDGliderViewController/RDGliderContentViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ContentViewController.h
3 | //
4 | // Created by Guillermo Delgado on 03/04/2017.
5 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | @interface RDGliderContentViewController : UIViewController
11 |
12 | /**
13 | shows a shadow behind the view, default value is NO
14 | */
15 | @property (nonatomic) BOOL showShadow;
16 |
17 | /**
18 | Sets a corner radius to the view, default value is 0.0f
19 | */
20 | @property (nonatomic) CGFloat cornerRadius;
21 |
22 |
23 | /**
24 | Constructor to define a fixed length view.
25 | */
26 | - (instancetype)initWithLength:(CGFloat)lenght;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/Example/GliderSampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/GliderSample/Helpers/RDButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // GAButton.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import "RDButton.h"
10 |
11 | @interface RDButton()
12 |
13 | @property (nonatomic) UIColor *bgColor;
14 |
15 | @end
16 |
17 | @implementation RDButton
18 |
19 | - (void)drawRect:(CGRect)rect {
20 | CGContextRef ctx = UIGraphicsGetCurrentContext();
21 | CGContextAddEllipseInRect(ctx, rect);
22 | CGContextSetFillColor(ctx, CGColorGetComponents([self.bgColor CGColor]));
23 | CGContextFillPath(ctx);
24 | }
25 |
26 | - (void)setBackgroundColor:(UIColor *)backgroundColor {
27 | _bgColor = [backgroundColor copy];
28 | super.backgroundColor = [UIColor clearColor];
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Guillermo RD
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/RDGliderViewController/RDGliderContentViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ContentViewController.m
3 | //
4 | // Created by Guillermo Delgado on 03/04/2017.
5 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
6 | //
7 |
8 | #import "RDGliderContentViewController.h"
9 |
10 | @interface RDGliderContentViewController ()
11 |
12 | @property (nonatomic) CGFloat lenght;
13 |
14 | @end
15 |
16 | @implementation RDGliderContentViewController
17 |
18 | - (instancetype)initWithLength:(CGFloat)lenght {
19 | if (self = [self init]) {
20 | _lenght = lenght;
21 | }
22 |
23 | return self;
24 | }
25 |
26 | - (void)viewDidLoad {
27 | [super viewDidLoad];
28 | self.view.frame = CGRectMake(0, 0, self.lenght, self.lenght);
29 |
30 | [self setCornerRadius:0.0f];
31 | [self setShowShadow:NO];
32 | }
33 |
34 | - (void)setShowShadow:(BOOL)showShadow {
35 | if (showShadow) {
36 | self.view.layer.shadowColor = [UIColor blackColor].CGColor;
37 | self.view.layer.shadowOpacity = 0.5f;
38 | self.view.layer.shadowRadius = 5.0f;
39 | self.view.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
40 | }
41 | }
42 |
43 | - (void)setCornerRadius:(CGFloat)cornerRadius {
44 | self.view.layer.cornerRadius = cornerRadius;
45 | [self.view.subviews firstObject].layer.cornerRadius = cornerRadius;
46 |
47 | }
48 | @end
49 |
--------------------------------------------------------------------------------
/Example/GliderSample/ContentViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ContentViewController.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
7 | //
8 |
9 | #import "ContentViewController.h"
10 |
11 | @interface ContentViewController ()
12 |
13 | @property (weak, nonatomic) IBOutlet UILabel *indexTopLabel;
14 | @property (weak, nonatomic) IBOutlet UILabel *offsetTopLabel;
15 | @property (weak, nonatomic) IBOutlet UILabel *indexBottomLabel;
16 | @property (weak, nonatomic) IBOutlet UILabel *offsetBottomLabel;
17 |
18 | @end
19 |
20 | @implementation ContentViewController
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 |
25 | self.showShadow = YES;
26 | self.cornerRadius = 10.0f;
27 | }
28 |
29 | - (void)setIndex:(NSUInteger)index ofMax:(NSUInteger)max {
30 | [self.indexTopLabel setText:[NSString stringWithFormat:@"Index (%lu of %lu)",(unsigned long)index , (unsigned long)max]];
31 | [self.indexBottomLabel setText:[NSString stringWithFormat:@"Index (%lu of %lu)",(unsigned long)index , (unsigned long)max]];
32 | }
33 |
34 | - (void)setOffset:(NSString *)offset {
35 | [self.offsetTopLabel setText:[NSString stringWithFormat:@"offset %@", offset]];
36 | [self.offsetBottomLabel setText:[NSString stringWithFormat:@"offset %@", offset]];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/Example/GliderSample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 0.1
19 | CFBundleVersion
20 | 76
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 | UIInterfaceOrientationPortraitUpsideDown
37 |
38 | UISupportedInterfaceOrientations~ipad
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationPortraitUpsideDown
42 | UIInterfaceOrientationLandscapeLeft
43 | UIInterfaceOrientationLandscapeRight
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/GliderSample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Example/GliderSample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/RDGliderViewController.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint RDGliderViewController.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'RDGliderViewController'
11 | s.version = '0.1.69'
12 | s.summary = 'Control for a draggable ViewController gliding over another ViewController.'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | RDGliderViewController is a view controller that manages a scrollable view using one side as a sliding point,
22 | choosing between Left-to-Right, Top-to-Bottom, Right-to-Left and Bottom-to-Top,
23 | and setting an array of offsets as percent values to determine the gliding view steps.
24 | DESC
25 |
26 | s.homepage = 'https://github.com/gelemias/RDGliderViewController'
27 | s.screenshots = 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/1.png',
28 | 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/2.png',
29 | 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/3.png',
30 | 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/4.png'
31 | s.license = { :type => 'MIT', :file => 'LICENSE' }
32 | s.author = { 'Guillermo RD' => 'gelemias@gmail.com' }
33 | s.source = { :git => 'https://github.com/gelemias/RDGliderViewController.git', :tag => s.version.to_s }
34 |
35 | s.ios.deployment_target = '8.0'
36 |
37 | s.source_files = 'RDGliderViewController/**/*'
38 |
39 | end
40 |
--------------------------------------------------------------------------------
/Example/GliderSample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | //- (void)applicationWillResignActive:(UIApplication *)application {
25 | // // 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.
26 | // // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | //}
28 | //
29 | //
30 | //- (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // // 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.
32 | // // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | //}
34 | //
35 | //
36 | //- (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | //}
39 | //
40 | //
41 | //- (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // // 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.
43 | //}
44 | //
45 | //
46 | //- (void)applicationWillTerminate:(UIApplication *)application {
47 | // // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | //}
49 | //
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## CHANGELOG
2 |
3 | #### Latest version - 0.1.76
4 | + 2017-11-22 *[FIX] Fixed issue happening with changing margin and starting from different offset than 0*
5 | + 2017-05-02 *[ENV] Added a before_install instruction to travis yml*
6 | + 2017-05-02 *[ENV] Updated ruby version required in gem file*
7 | + 2017-04-27 *[REF] refactored Scrollview to be aligned with the Swift project*
8 |
9 | #### Previously released:
10 | + 2017-04-21 *[REF] created a base RDGliderContentViewController meant to be the base case for any content view controller*
11 | + 2017-04-21 *[FIX] Fixed hidden behaviour with inverted orientationTypes*
12 | + 2017-04-21 *[POD] updated podspec*
13 | + 2017-04-21 *[DOC] Completed a basic documentation to the README and added a CHANGELOG*
14 | + 2017-04-20 *[DOC] Update README with CocoaPods and Carthage support*
15 | + 2017-04-20 *[ENV] preparing project for a pod lib*
16 | + 2017-04-18 *[ENV] Added code coverage support*
17 | + 2017-04-18 *[ENV] added yml build file for Travis CI*
18 | + 2017-04-18 *[TEST] Added unit tests for GVScrollView*
19 | + 2017-04-14 *[REF] enhanced naming*
20 | + 2017-04-14 *[FIX] issue with rendering shadow when rotating*
21 | + 2017-04-14 *[FIX] horizontal offset broken*
22 | + 2017-04-13 *[DEV] first implementation of Left To Right side*
23 | + 2017-04-13 *[REF] cleaned constraints of scrollview*
24 | + 2017-04-13 *[DEV] limited max content scroll to largest offset percent of content length*
25 | + 2017-04-13 *[DEV] added margins to reversed positions*
26 | + 2017-04-13 *[DEV] first implementation for TopToBottom GlideView*
27 | + 2017-04-12 *[FIX] fixed release notes not recognised characters*
28 | + 2017-04-12 *[DOC] added ReleaseNotes to Readme file*
29 | + 2017-04-07 *[DEV] enhanced threshold calculation for boundaries*
30 | + 2017-04-07 *[DOC] enhanced documentation on headers*
31 | + 2017-04-07 *[DEV] improved constructor of GlideViewController*
32 | + 2017-04-07 *[FIX] offset changes where altered while rotating*
33 | + 2017-04-06 *[REF] refactored contentSize handling when rotating*
34 | + 2017-04-06 *[FIX] offset is affected by the margin when rotating a rather large view*
35 | + 2017-04-06 *[DEV] enable the possibility to change multiple offsets by dragging*
36 | + 2017-04-06 *[REF] changed offsets approach to % dependant on content’s view*
37 | + 2017-04-06 *[REF] refactor usage of glideView types*
38 | + 2017-04-05 *[DEV] enhanced recalculation of offset when rotating scrollview*
39 | + 2017-04-05 *[FIX] landscape margins offset is off*
40 | + 2017-04-05 *[FIX] landscape to portrait rotation changes offset of scrollview*
41 | + 2017-04-05 *[FIX] landscape margins offset is off*
42 |
--------------------------------------------------------------------------------
/RDGliderViewController/RDScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDScrollView.h
3 | // GliderSample
4 | //
5 | // Created by GuillermoD on 8/3/16.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 | typedef NS_ENUM(unsigned int, RDScrollViewOrientationType) {
11 | RDScrollViewOrientationUnknown = 0,
12 |
13 | RDScrollViewOrientationLeftToRight,
14 | RDScrollViewOrientationBottomToTop,
15 | RDScrollViewOrientationRightToLeft,
16 | RDScrollViewOrientationTopToBottom
17 |
18 | };
19 |
20 | @interface RDScrollView : UIScrollView
21 |
22 | /**
23 | DraggableContainer
24 | */
25 | @property (nonatomic) UIView *content;
26 |
27 | /**
28 | Orientation for draggable container.
29 | Default value : RDScrollViewOrientationLeftToRight
30 | */
31 | @property (nonatomic) RDScrollViewOrientationType orientationType;
32 |
33 | /**
34 | Expandable offset in % of content view. from 0 to 1.
35 | */
36 | @property (nonatomic, copy) NSArray *offsets;
37 |
38 | /**
39 | Determines whether the element's offset is different than % 0.
40 | */
41 | @property (nonatomic, readonly) BOOL isOpen;
42 |
43 | /**
44 | Returns the position of open Offsets.
45 | */
46 | @property (nonatomic, readonly) NSUInteger offsetIndex;
47 |
48 | /**
49 | Margin of elastic animation default is 20px.
50 | */
51 | @property (nonatomic) CGFloat margin;
52 |
53 | /**
54 | Consider subviews of the content as part of the content, used when dragging.
55 | Default Value is False
56 | */
57 | @property (nonatomic) BOOL selectContentSubViews;
58 |
59 | /**
60 | Duration of animation for changing offset, default vaule is 0.3
61 | */
62 | @property (nonatomic) CGFloat duration;
63 |
64 | /**
65 | Delay of animation for changing offset, default vaule is 0.0
66 | */
67 | @property (nonatomic) CGFloat delay;
68 |
69 | /**
70 | Damping of animation for changing offset, default vaule is 0.7
71 | */
72 | @property (nonatomic) CGFloat damping;
73 |
74 | /**
75 | Damping of animation for changing offset, default vaule is 0.6
76 | */
77 | @property (nonatomic) CGFloat velocity;
78 |
79 | /**
80 | Call this method to force recalculation of contentSize in ScrollView, i.e. when content changes.
81 | */
82 | - (void)recalculateContentSize;
83 |
84 | // Methods to Increase or decrease offset of content within RDScrollView.
85 |
86 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated completion:(void (^)(BOOL finished))completion;
87 | - (void)expandWithCompletion:(void (^)(BOOL finished))completion;
88 | - (void)collapseWithCompletion:(void (^)(BOOL finished))completion;
89 | - (void)closeWithCompletion:(void (^)(BOOL finished))completion;
90 |
91 | // Unavailable constructors.
92 |
93 | - (instancetype)init NS_UNAVAILABLE;
94 | - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
95 |
96 | @end
97 |
--------------------------------------------------------------------------------
/Example/GliderSample.xcodeproj/xcshareddata/xcschemes/GliderSample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
65 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
84 |
86 |
92 |
93 |
94 |
95 |
97 |
98 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/RDGliderViewController/RDGliderViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDGliderViewController.h
3 | // GliderSample
4 | //
5 | // Created by GuillermoD on 8/4/16.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RDScrollView.h"
11 | #import "RDGliderContentViewController.h"
12 |
13 | @class RDGliderViewController;
14 |
15 | @protocol RDGliderViewControllerDelegate
16 |
17 | @optional
18 |
19 | /**
20 | Delegate method to notify invoke object when offset has changed
21 | */
22 | - (void)glideViewController:(nonnull RDGliderViewController *)glideViewController hasChangedOffsetOfContent:(CGPoint)offset;
23 |
24 | /**
25 | Delegate method to notify invoke object when glideView will increase offset by one
26 | */
27 | - (void)glideViewControllerWillExpand:(nonnull RDGliderViewController *)glideViewController;
28 |
29 | /**
30 | Delegate method to notify invoke object when glideView will decrease offset by one
31 | */
32 | - (void)glideViewControllerWillCollapse:(nonnull RDGliderViewController *)glideViewController;
33 |
34 | /**
35 | Delegate method to notify invoke object when glideView did increase offset by one
36 | */
37 | - (void)glideViewControllerDidExpand:(nonnull RDGliderViewController *)glideViewController;
38 |
39 | /**
40 | Delegate method to notify invoke object when glideView did decrease offset by one
41 | */
42 | - (void)glideViewControllerDidCollapse:(nonnull RDGliderViewController *)glideViewController;
43 |
44 | @end
45 |
46 |
47 | @interface RDGliderViewController : UIViewController
48 |
49 | @property (nonatomic, weak, nullable) id delegate;
50 |
51 | @property (nonatomic, readonly, nonnull) UIViewController *contentViewController;
52 |
53 | /**
54 | Margin of elastic animation default is 20px;
55 | */
56 | @property (nonatomic) CGFloat marginOffset;
57 |
58 | /**
59 | Sorted list of offsets in % of contentVC view. from 0 to 1
60 | */
61 | @property (nonatomic, copy, nonnull) NSArray *offsets;
62 |
63 | /**
64 | Orientation type of the glide view
65 | */
66 | @property (nonatomic, readonly) RDScrollViewOrientationType orientationType;
67 |
68 | /**
69 | Current offset of the glide view
70 | */
71 | @property (nonatomic, readonly) NSUInteger currentOffsetIndex;
72 |
73 | /**
74 | Returns a bool for determining if the glide view isn't closed, is different than offset % 0.
75 | */
76 | @property (nonatomic, readonly) BOOL isOpen;
77 |
78 | /**
79 | Bool meant for enabling the posibility to close the glide view dragging, Default value is NO;
80 | */
81 | @property (nonatomic) BOOL disableDraggingToClose;
82 |
83 | /**
84 | Change contentViewController type and offsets after the VC has been initialized.
85 | * @param contentViewController external ViewController placed as a content of the GlideView
86 | * @param type of GlideView Left to Right, Right to Left, Bottom To Top and Top to Bottom.
87 | * @param offsets Array of offsets in % (0 to 1) dependent of Content size if not expecified UIScreen */
88 | - (void)setContentViewController:(nonnull RDGliderContentViewController *)contentViewController
89 | type:(RDScrollViewOrientationType)type
90 | offsets:(nonnull NSArray *)offsets;
91 |
92 | /**
93 | This method gives a shake to the Gliver view, is meant to grap users atention.
94 | */
95 | - (void)shake;
96 |
97 | /**
98 | Increase the position of the Gliver view by one in the list of offsets
99 | */
100 | - (void)expand;
101 |
102 | /**
103 | Decrease the position of the Gliver view by one in the list of offsets
104 | */
105 | - (void)collapse;
106 |
107 | /**
108 | Change offset of view.
109 | * @param offsetIndex setNew Offset of GlideView, parameter needs to be within offsets Array count list.
110 | * @param animated animates the offset change
111 | */
112 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated;
113 |
114 | /**
115 | This method moves the View directly to the first offset which is the default position.
116 | */
117 | - (void)close;
118 |
119 | /**
120 | Initializator of the object, it requires the parent view controller to build its components
121 | * @param parent Parent Class of this instance
122 | * @param content external ViewController placed as a content of the GlideView
123 | * @param type of GlideView Left to Right, Right to Left, Bottom To Top and Top to Bottom.
124 | * @param offsets Array of offsets in % (0 to 1) dependent of Content size if not expecified UIScreen bounds.
125 | * @return A newly created RDGliderViewController instance
126 | */
127 | - (nullable instancetype)initOn:(nonnull UIViewController *)parent
128 | WithContent:(nonnull RDGliderContentViewController *)content
129 | type:(RDScrollViewOrientationType)type
130 | AndOffsets:(nonnull NSArray *)offsets;
131 |
132 | #pragma mark - Unavailable methods
133 |
134 | - (nullable instancetype)init NS_UNAVAILABLE;
135 | - (nullable instancetype)initWithCoder:(NSCoder *_Nullable)aDecoder NS_UNAVAILABLE;
136 | - (nonnull instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE;
137 |
138 | @end
139 |
--------------------------------------------------------------------------------
/Example/GliderSample/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "RDGliderViewController.h"
11 | #import "ContentViewController.h"
12 | #import "RDGradientView.h"
13 |
14 | @interface ViewController ()
15 |
16 | @property (nonatomic) RDGliderViewController *leftToRightGlideVC;
17 | @property (nonatomic) RDGliderViewController *bottomToTopGlideVC;
18 | @property (nonatomic) RDGliderViewController *topToBottomGlideVC;
19 | @property (nonatomic) RDGliderViewController *rightToLeftGlideVC;
20 |
21 | @end
22 |
23 | @implementation ViewController
24 |
25 | - (void)viewDidLoad {
26 | [super viewDidLoad];
27 |
28 | RDGradientView *bgGradient = [[RDGradientView alloc] initWithFrame:self.view.bounds];
29 | bgGradient.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
30 | bgGradient.layer.colors = @[(id)[UIColor colorWithRed:(CGFloat)0.643 green:(CGFloat)0.569 blue:(CGFloat)0.776 alpha:1].CGColor,
31 | (id)[UIColor colorWithRed:(CGFloat)(CGFloat)0.573 green:(CGFloat)0.875 blue:(CGFloat)0.678 alpha:1].CGColor];
32 | [self.view insertSubview:bgGradient atIndex:0];
33 |
34 | [self initRightToLeftGlideView];
35 | [self initBottomToTopGlideView];
36 | [self initTopToBottomGlideView];
37 | [self initLeftToRightGlideView];
38 | }
39 |
40 | - (void)initRightToLeftGlideView{
41 | self.rightToLeftGlideVC = [[RDGliderViewController alloc] initOn:self
42 | WithContent:[[ContentViewController alloc] initWithLength:200.0f]
43 | type:RDScrollViewOrientationRightToLeft
44 | AndOffsets:@[@(0), @(1)]];
45 | self.rightToLeftGlideVC.delegate = self;
46 | }
47 |
48 | - (void)initBottomToTopGlideView {
49 | self.bottomToTopGlideVC = [[RDGliderViewController alloc] initOn:self
50 | WithContent:[ContentViewController new]
51 | type:RDScrollViewOrientationBottomToTop
52 | AndOffsets:@[@(0),
53 | @(0.6),
54 | @(0.2),
55 | @(0.4),
56 | @(0.8)]];
57 | self.bottomToTopGlideVC.delegate = self;
58 | self.bottomToTopGlideVC.marginOffset = 30;
59 | }
60 |
61 | - (void)initTopToBottomGlideView {
62 | self.topToBottomGlideVC = [[RDGliderViewController alloc] initOn:self
63 | WithContent:[[ContentViewController alloc] initWithLength:400.0f]
64 | type:RDScrollViewOrientationTopToBottom
65 | AndOffsets:@[@(0),
66 | @(0.5),
67 | @(1)]];
68 | self.topToBottomGlideVC.delegate = self;
69 | }
70 |
71 | - (void)initLeftToRightGlideView {
72 | self.leftToRightGlideVC = [[RDGliderViewController alloc] initOn:self
73 | WithContent:[ContentViewController new]
74 | type:RDScrollViewOrientationLeftToRight
75 | AndOffsets:@[@(0),
76 | @(0.6),
77 | @(0.2),
78 | @(0.4),
79 | @(0.8),
80 | @(1)]];
81 | self.leftToRightGlideVC.delegate = self;
82 | self.leftToRightGlideVC.marginOffset = 10;
83 | }
84 |
85 | #pragma mark - Actions
86 |
87 | - (IBAction)bottomToTopButtonPressed:(id)sender {
88 | if ([self.bottomToTopGlideVC currentOffsetIndex] < [[self.bottomToTopGlideVC offsets] count] - 1) {
89 | [self.bottomToTopGlideVC expand];
90 | } else {
91 | [self.bottomToTopGlideVC shake];
92 | }
93 | }
94 |
95 | - (IBAction)leftToRightButtonPressed:(id)sender {
96 | if ([self.leftToRightGlideVC currentOffsetIndex] < [[self.leftToRightGlideVC offsets] count] - 1) {
97 | [self.leftToRightGlideVC expand];
98 | } else {
99 | [self.leftToRightGlideVC shake];
100 | }
101 | }
102 |
103 | - (IBAction)topToBottomButtonPressed:(id)sender {
104 | if ([self.topToBottomGlideVC currentOffsetIndex] < [[self.topToBottomGlideVC offsets] count] - 1) {
105 | [self.topToBottomGlideVC expand];
106 | } else {
107 | [self.topToBottomGlideVC shake];
108 | }
109 | }
110 |
111 | - (IBAction)rightToLeftButtonPressed:(id)sender {
112 | if ([self.rightToLeftGlideVC currentOffsetIndex] < [[self.rightToLeftGlideVC offsets] count] - 1) {
113 | [self.rightToLeftGlideVC expand];
114 | } else {
115 | [self.rightToLeftGlideVC shake];
116 | }
117 | }
118 |
119 | #pragma mark - GlideViewControllerDelegate
120 |
121 | - (void)glideViewController:(RDGliderViewController *)glideViewController hasChangedOffsetOfContent:(CGPoint)offset {
122 | ContentViewController *vc = (ContentViewController *)glideViewController.contentViewController;
123 | [vc setOffset:NSStringFromCGPoint(offset)];
124 | }
125 |
126 | - (void)glideViewControllerDidExpand:(RDGliderViewController *)glideViewController {
127 | ContentViewController *vc = (ContentViewController *)glideViewController.contentViewController;
128 | [vc setIndex:glideViewController.currentOffsetIndex ofMax:[glideViewController.offsets count] - 1];
129 | }
130 |
131 | - (void)glideViewControllerDidCollapse:(RDGliderViewController *)glideViewController {
132 | ContentViewController *vc = (ContentViewController *)glideViewController.contentViewController;
133 | [vc setIndex:glideViewController.currentOffsetIndex ofMax:[glideViewController.offsets count] - 1];
134 | }
135 |
136 | @end
137 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🛫 RDGliderViewController 🛬
2 |
3 | [](https://travis-ci.org/gelemias/RDGliderViewController) [](https://coveralls.io/github/gelemias/RDGliderViewController?branch=develop) [](https://opensource.org/licenses/MIT) [](https://github.com/Carthage/Carthage)
4 |
5 |
6 | Control in *Objective-C* for a draggable ViewController gliding over another ViewController.
7 |
8 | **⚠️⚠️ This project is discontinued, use [RDGliderViewController-Swift](https://github.com/gelemias/RDGliderViewController-Swift) instead ⚠️⚠️**
9 |
10 | RDGliderViewController is a view controller that manages a scrollable view using one side as a sliding point, choosing between Left-to-Right, Top-to-Bottom, Right-to-Left and Bottom-to-Top, and setting an array of offsets as percent values to determine the gliding view steps, as described bellow.
11 |
12 |
13 |
14 | ## Example
15 |
16 | A good way to learn how to use RDGliderViewController is to go through the example app. Take a look at the tests as well for a more thorough usage.
17 |
18 | To run the example project, clone the repo, and open `./Example/GliderSample.xcodeproj`
19 |
20 | ## Installation
21 |
22 | ### Using CocoaPods
23 | RDGliderViewController is available through [CocoaPods](http://cocoapods.org), a dependency manager for Swift and Objective-C Cocoa projects. To install
24 | it, simply add the following line to your [Podfile](https://guides.cocoapods.org/using/getting-started.html):
25 |
26 | ```ruby
27 | pod 'RDGliderViewController'
28 | ```
29 |
30 | ### Using Carthage
31 |
32 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
33 |
34 | You can install Carthage with [Homebrew](http://brew.sh/) and then integrate **RDGliderViewController** into your Xcode project by adding the following line to your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile):
35 |
36 | ```ruby
37 | github "gelemias/RDGliderViewController"
38 | ```
39 |
40 | ### Manual Installation
41 |
42 | For an old fashion installation you can directly add the **header** and **implementation** files for `RDGliderViewController` and `RDScrollView` to your project.
43 |
44 | - Download the latest code version or add the repository as a git submodule to your git-tracked project.
45 |
46 | - Drag and drop the folder `RDGliderViewController/` onto your project and make sure to select **Copy items if needed**.
47 |
48 | - Add `#import "RDGliderViewController.h"` at the beginning of your class.
49 |
50 | ## Usage
51 |
52 | RDGliderViewController is a very simple to use controller that mainly depends on three values to work on, *Orientation type* which is determines the scrolling side, the *List of Offsets* which represent the steps of expansion and contraction and the content itself with is a view controller completely independent.
53 |
54 | ### Creation
55 |
56 | Use a the following custom init to instantiate RDGliderViewController:
57 |
58 | `- (instancetype)initOn:(nonnull UIViewController *)parent WithContent:(nonnull RDGliderContentViewController *)content type:(RDScrollViewOrientationType)type AndOffsets:(nonnull NSArray *)offsets {
59 | `
60 |
61 | e.g.
62 | ```Objective-C
63 | RDGliderViewController *gliderVC = [[RDGliderViewController alloc] initOn:self
64 | WithContent:[UIViewController new]
65 | type:RDScrollViewOrientationRightToLeft
66 | AndOffsets:@[@0.2, @0.5, @1]];
67 | ```
68 |
69 | And that's all you need to do, if you run your app, an instance of UIViewController should be scrollable from Right to Left on top of `self` view controller.
70 |
71 | ### Content and Container
72 |
73 | The content view controller should inherit from RDGliderContentViewController to inherit the properly resize when rotating, is treated as a child view controller of this one.
74 |
75 | ```Objective-C
76 | #import "RDGliderContentViewController.h"
77 |
78 | @interface ContentViewController : RDGliderContentViewController
79 |
80 | // Your content class Here
81 |
82 | @end
83 |
84 | ```
85 |
86 | Mentioned that not much more is required, only keep in mind that if you do not define a size for the view, RDGliderViewController will it resize it to match container's.
87 |
88 | Content view controller can be afterwards as well but always along offsets and orientation since these three properties are dependent of each other:
89 |
90 | `- (void)setContentViewController:(nonnull RDGliderContentViewController *)contentViewController
91 | type:(RDScrollViewOrientationType)type
92 | offsets:(nonnull NSArray *)offsets;
93 | `
94 |
95 | ### Orientation types
96 |
97 | `RDScrollViewOrientationType` represent the four sides of the display plus the sliding direction.
98 |
99 | ```Objective-C
100 | typedef NS_ENUM(unsigned int, RDScrollViewOrientationType) {
101 | RDScrollViewOrientationUnknown = 0,
102 |
103 | RDScrollViewOrientationLeftToRight,
104 | RDScrollViewOrientationBottomToTop,
105 | RDScrollViewOrientationRightToLeft,
106 | RDScrollViewOrientationTopToBottom
107 |
108 | };
109 | ```
110 |
111 | if content view controller defines a fixed size, this size will be respected during rotation, for horizontal scrolls (Left-to-Right and Right-to-Left) will resize *Width* and for Vertical (Bottom-to-Top and Top-to-Bottom) will reize *Height*
112 |
113 |
114 | ### Offsets and OffsetIndex
115 |
116 | this `NSArray *offsets` property is a list of steps used when either expanding / collapsing or dragging the list is represented in percent % related with the length of the content view controller, e.g. given the list [0, 0.5, 1] for a lenght *400px* would be this offset steps of [0px, 200px, 400px]
117 |
118 | `offsets[OffsetIndex].floatValue * contentViewController.lenght`
119 |
120 | For inverted OrientationTypes (RDScrollViewOrientationLeftToRight and RDScrollViewOrientationTopToBottom), the offset list are also inverted so for example for a list [0.2, 0.4, 0.6, 1] will be returned [0.8, 0.6, 0.4, 0]
121 |
122 | ### Expand, Collapse and Close
123 |
124 | By default RDGliderViewController is draggable but when setting an offset of `0.0f%` it will be outside of the screen on starting point, to Increase the position of the view by one in the list of offsets use:
125 |
126 | `- (void)expand;`
127 |
128 | to Decrease the position of RDGliderViewController by one in the list of offsets:
129 |
130 | `- (void)collapse;`
131 |
132 | and *close* which moves the View directly to the first offset which is the default position:
133 |
134 | `- (void)close;`
135 |
136 | All of these methods simple change the offsetIndex when calling this method.
137 |
138 | `- (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated;`
139 |
140 | ## License
141 |
142 | RDGliderViewController is released under the MIT license. See [LICENSE](https://github.com/gelemias/RDGliderViewController/blob/develop/LICENSE) for details.
143 |
144 |
145 | ## Change log
146 |
147 | See [CHANGELOG](https://github.com/gelemias/RDGliderViewController/blob/develop/CHANGELOG.md) for details.
148 |
--------------------------------------------------------------------------------
/Example/GliderSampleTests/RDScrollViewTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // GlideViewTests.m
3 | // GlideViewTests
4 | //
5 | // Created by Guillermo Delgado on 03/04/2017.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RDScrollView.h"
11 |
12 | @interface RDScrollView()
13 |
14 | @property (nonatomic) NSUInteger offsetIndex;
15 |
16 | @end
17 |
18 | @interface RDScrollViewTests : XCTestCase
19 |
20 | @property (nonatomic) RDScrollView *rdScrollView;
21 |
22 | @end
23 |
24 | @implementation RDScrollViewTests
25 |
26 | - (void)setUp {
27 | [super setUp];
28 | self.rdScrollView = [[RDScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
29 | }
30 |
31 | - (void)tearDown {
32 | self.rdScrollView = nil;
33 | [super tearDown];
34 | }
35 |
36 | - (void)testInit {
37 | XCTAssertTrue(self.rdScrollView.margin == 20);
38 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationRightToLeft);
39 | XCTAssertFalse(self.rdScrollView.showsVerticalScrollIndicator);
40 | XCTAssertFalse(self.rdScrollView.showsHorizontalScrollIndicator);
41 | XCTAssertFalse(self.rdScrollView.bounces);
42 | XCTAssertTrue(self.rdScrollView.directionalLockEnabled);
43 | XCTAssertFalse(self.rdScrollView.pagingEnabled);
44 | XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(self.rdScrollView.contentInset, UIEdgeInsetsZero));
45 | XCTAssertTrue(self.rdScrollView.decelerationRate == UIScrollViewDecelerationRateFast);
46 | }
47 |
48 | - (void)testExpandOffset {
49 | XCTestExpectation *ex0 = [self expectationWithDescription:@"Success0"];
50 |
51 | [self.rdScrollView expandWithCompletion:^(BOOL finished) {
52 | if (finished) {
53 | [ex0 fulfill];
54 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
55 | }
56 | }];
57 |
58 | [self.rdScrollView setOffsets:@[@0,@.5,@1]];
59 |
60 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3);
61 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
62 |
63 | XCTestExpectation *ex1 = [self expectationWithDescription:@"Success1"];
64 |
65 | [self.rdScrollView expandWithCompletion:^(BOOL finished) {
66 | if (finished) {
67 | [ex1 fulfill];
68 | XCTAssertTrue([self.rdScrollView offsetIndex] == 1);
69 | }
70 | }];
71 |
72 | [self waitForExpectations:@[ex0, ex1] timeout:2];
73 |
74 | XCTAssertTrue([self.rdScrollView offsetIndex] == 1);
75 |
76 | XCTestExpectation *ex2 = [self expectationWithDescription:@"Success2"];
77 |
78 | [self.rdScrollView expandWithCompletion:^(BOOL finished) {
79 | if (finished) {
80 | [ex2 fulfill];
81 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2);
82 | }
83 | }];
84 |
85 | [self waitForExpectations:@[ex2] timeout:2];
86 |
87 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2);
88 |
89 | XCTestExpectation *ex3 = [self expectationWithDescription:@"Success3"];
90 |
91 | [self.rdScrollView expandWithCompletion:^(BOOL finished) {
92 | if (finished) {
93 | [ex3 fulfill];
94 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2);
95 | }
96 | }];
97 |
98 | [self waitForExpectations:@[ex3] timeout:2];
99 | }
100 |
101 | - (void)testCollapseOffset {
102 | [self.rdScrollView setOffsets:@[@0,@.5,@1]];
103 | [self.rdScrollView setOffsetIndex:[[self.rdScrollView offsets] count] - 1];
104 |
105 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3);
106 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2);
107 |
108 | XCTestExpectation *ex1 = [self expectationWithDescription:@"Success1"];
109 |
110 | [self.rdScrollView collapseWithCompletion:^(BOOL finished) {
111 | if (finished) {
112 | [ex1 fulfill];
113 | XCTAssertTrue([self.rdScrollView offsetIndex] == 1);
114 | }
115 | }];
116 |
117 | [self waitForExpectations:@[ex1] timeout:2];
118 |
119 | XCTestExpectation *ex2 = [self expectationWithDescription:@"Success2"];
120 |
121 | [self.rdScrollView collapseWithCompletion:^(BOOL finished) {
122 | if (finished) {
123 | [ex2 fulfill];
124 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
125 | }
126 | }];
127 |
128 | [self waitForExpectations:@[ex2] timeout:2];
129 | XCTestExpectation *ex3 = [self expectationWithDescription:@"Success3"];
130 |
131 | [self.rdScrollView collapseWithCompletion:^(BOOL finished) {
132 | if (finished) {
133 | [ex3 fulfill];
134 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
135 | }
136 | }];
137 |
138 | [self waitForExpectations:@[ex3] timeout:2];
139 | }
140 |
141 | - (void)testCloseOffset {
142 | XCTestExpectation *ex0 = [self expectationWithDescription:@"Success0"];
143 |
144 | [self.rdScrollView closeWithCompletion:^(BOOL finished) {
145 | if (finished) {
146 | [ex0 fulfill];
147 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
148 | }
149 | }];
150 |
151 | [self.rdScrollView setOffsets:@[@0,@.5,@1]];
152 |
153 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3);
154 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
155 |
156 | XCTestExpectation *ex1 = [self expectationWithDescription:@"Success1"];
157 |
158 | [self.rdScrollView closeWithCompletion:^(BOOL finished) {
159 | if (finished) {
160 | [ex1 fulfill];
161 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
162 | }
163 | }];
164 |
165 | [self waitForExpectations:@[ex0, ex1] timeout:2];
166 |
167 | [self.rdScrollView setOffsetIndex:[[self.rdScrollView offsets] count] - 1];
168 |
169 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3);
170 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2);
171 |
172 | XCTestExpectation *ex2 = [self expectationWithDescription:@"Success2"];
173 |
174 | [self.rdScrollView closeWithCompletion:^(BOOL finished) {
175 | if (finished) {
176 | [ex2 fulfill];
177 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0);
178 | }
179 | }];
180 |
181 | [self waitForExpectations:@[ex2] timeout:2];
182 | }
183 |
184 | - (void)testOffsets {
185 | NSArray *ar = @[@0, @3, @0.123];
186 | XCTAssertThrows([self.rdScrollView setOffsets:ar]);
187 |
188 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationRightToLeft);
189 |
190 | ar = @[@1, @0.6, @0.3, @0];
191 | [self.rdScrollView setOffsets:ar];
192 |
193 | XCTAssertTrue([[self.rdScrollView offsets] isEqualToArray:
194 | [ar sortedArrayUsingSelector:@selector(compare:)]]);
195 |
196 | [self.rdScrollView setOrientationType:RDScrollViewOrientationBottomToTop];
197 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationBottomToTop);
198 |
199 | ar = @[@1, @0.4, @0.3, @0.4];
200 | [self.rdScrollView setOffsets:ar];
201 |
202 | NSArray *aux = @[@0.3, @0.4, @1];
203 | XCTAssertTrue([[self.rdScrollView offsets] isEqualToArray:aux]);
204 |
205 | [self.rdScrollView setOrientationType:RDScrollViewOrientationLeftToRight];
206 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationLeftToRight);
207 |
208 | [self.rdScrollView setOffsets:ar];
209 |
210 | aux = @[@0.7, @0.6, @0];
211 |
212 | for (int i = 0 ; i < [self.rdScrollView.offsets count] - 1 ; i++) {
213 | XCTAssertTrue(self.rdScrollView.offsets[i].floatValue == aux[i].floatValue);
214 | }
215 |
216 | [self.rdScrollView setOrientationType:RDScrollViewOrientationTopToBottom];
217 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationTopToBottom);
218 |
219 | ar = @[@0, @0.5, @1];
220 | [self.rdScrollView setOffsets:ar];
221 |
222 | aux = @[@1, @0.5, @0];
223 | XCTAssertTrue([[self.rdScrollView offsets] isEqualToArray:aux]);
224 | }
225 |
226 | @end
227 |
--------------------------------------------------------------------------------
/Example/GliderSample/ContentViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
35 |
41 |
47 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Example/GliderSampleTests/RDGliderViewControllerTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDGliderViewController.m
3 | // GliderSample
4 | //
5 | // Created by Guillermo Delgado on 14/04/2017.
6 | // Copyright © 2017 Guillermo Delgado. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RDGliderViewController.h"
11 |
12 | @interface RDGliderViewController ()
13 |
14 | @property (nonatomic) RDScrollView *scrollView;
15 |
16 | - (NSUInteger)nearestOffsetIndexTo:(CGPoint)contentOffset;
17 |
18 | @end
19 |
20 | @interface RDGliderViewControllerTests : XCTestCase {
21 | XCTestExpectation *_testExpWill;
22 | XCTestExpectation *_testExpDid;
23 | }
24 |
25 | @property (nonatomic) RDGliderViewController *gliderVC;
26 |
27 | @end
28 |
29 | @implementation RDGliderViewControllerTests
30 |
31 | - (void)setUp {
32 | [super setUp];
33 |
34 | self.gliderVC = [[RDGliderViewController alloc] initOn:[UIViewController new]
35 | WithContent:[UIViewController new]
36 | type:RDScrollViewOrientationRightToLeft
37 | AndOffsets:@[@0, @1]];
38 | self.gliderVC.delegate = self;
39 | XCTAssertNotNil(self.gliderVC);
40 | }
41 |
42 | - (void)tearDown {
43 | self.gliderVC = nil;
44 | [super tearDown];
45 | }
46 |
47 | - (void)testOffsets {
48 | NSArray *ar = @[@0, @1];
49 | XCTAssertTrue([[self.gliderVC offsets] isEqualToArray:ar]);
50 | XCTAssertThrows([self.gliderVC setOffsets:@[]]);
51 |
52 | [self.gliderVC setOffsets:@[@0, @0.5, @1]];
53 | XCTAssertTrue(self.gliderVC.marginOffset == self.gliderVC.scrollView.margin);
54 |
55 |
56 | self.gliderVC.scrollView = nil;
57 | XCTAssertThrows([self.gliderVC setOffsets:@[@0]]);
58 | XCTAssertThrows([self.gliderVC offsets]);
59 | XCTAssertTrue(self.gliderVC.currentOffsetIndex == 0);
60 | XCTAssertTrue(self.gliderVC.marginOffset == 0.0f);
61 | }
62 |
63 | - (void)testOrientationType {
64 | XCTAssertTrue([self.gliderVC orientationType] == RDScrollViewOrientationRightToLeft);
65 |
66 | self.gliderVC.scrollView = nil;
67 | XCTAssertTrue([self.gliderVC orientationType] == RDScrollViewOrientationUnknown);
68 | }
69 |
70 | - (void)testContentViewController {
71 |
72 | XCTAssertThrows([self.gliderVC setContentViewController:[self nilReturnMethod]
73 | type:RDScrollViewOrientationUnknown
74 | offsets:@[@1]]);
75 |
76 | XCTAssertThrows([self.gliderVC setContentViewController:[UIViewController new]
77 | type:RDScrollViewOrientationUnknown
78 | offsets:[self nilReturnMethod]]);
79 |
80 | UIViewController *contentVC = [UIViewController new];
81 | [self.gliderVC setContentViewController:contentVC
82 | type:RDScrollViewOrientationRightToLeft
83 | offsets:@[@1]];
84 | XCTAssertTrue(self.gliderVC.scrollView.orientationType == RDScrollViewOrientationRightToLeft);
85 | XCTAssertTrue([self.gliderVC.scrollView.offsets isEqualToArray:@[@1]]);
86 | }
87 |
88 | - (void)testShake {
89 | CGRect frame = self.gliderVC.scrollView.frame;
90 |
91 | [self.gliderVC shake];
92 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame));
93 |
94 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationLeftToRight;
95 | [self.gliderVC shake];
96 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame));
97 |
98 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationBottomToTop;
99 | [self.gliderVC shake];
100 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame));
101 |
102 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationTopToBottom;
103 | [self.gliderVC shake];
104 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame));
105 | }
106 |
107 | - (void)testExpand {
108 | _testExpWill = [self expectationWithDescription:@"Will"];
109 | _testExpDid = [self expectationWithDescription:@"Did"];
110 |
111 | _testExpWill.assertForOverFulfill = NO;
112 | _testExpDid.assertForOverFulfill = NO;
113 |
114 | [self.gliderVC expand];
115 |
116 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2];
117 | XCTAssertTrue([self.gliderVC isOpen]);
118 | }
119 |
120 | - (void)testCollapse {
121 | _testExpWill = [self expectationWithDescription:@"Will"];
122 | _testExpDid = [self expectationWithDescription:@"Did"];
123 |
124 | _testExpWill.assertForOverFulfill = NO;
125 | _testExpDid.assertForOverFulfill = NO;
126 |
127 | [self.gliderVC collapse];
128 |
129 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2];
130 | }
131 |
132 | - (void)testClose {
133 | _testExpWill = [self expectationWithDescription:@"Will"];
134 | _testExpDid = [self expectationWithDescription:@"Did"];
135 |
136 | _testExpWill.assertForOverFulfill = NO;
137 | _testExpDid.assertForOverFulfill = NO;
138 |
139 | [self.gliderVC close];
140 |
141 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2];
142 | XCTAssertFalse([self.gliderVC isOpen]);
143 | }
144 |
145 | - (void)testChangeOffset {
146 | _testExpWill = [self expectationWithDescription:@"Will"];
147 | _testExpDid = [self expectationWithDescription:@"Did"];
148 |
149 | _testExpWill.assertForOverFulfill = NO;
150 | _testExpDid.assertForOverFulfill = NO;
151 |
152 | XCTAssertTrue([self.gliderVC currentOffsetIndex] == 0);
153 |
154 | [self.gliderVC setOffsets:@[@1, @0.5, @0]];
155 | [self.gliderVC changeOffsetTo:2 animated:NO];
156 |
157 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2];
158 | XCTAssertTrue([self.gliderVC isOpen]);
159 | XCTAssertTrue([self.gliderVC currentOffsetIndex] == 2);
160 |
161 | _testExpWill = [self expectationWithDescription:@"Will"];
162 | _testExpDid = [self expectationWithDescription:@"Did"];
163 |
164 | [self.gliderVC setOffsets:@[@1, @0.5, @0]];
165 | [self.gliderVC changeOffsetTo:0 animated:NO];
166 |
167 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2];
168 | XCTAssertFalse([self.gliderVC isOpen]);
169 | XCTAssertTrue([self.gliderVC currentOffsetIndex] == 0);
170 | }
171 |
172 | - (void)testNearestOffsetIndex {
173 | CGPoint point = CGPointMake(0, 0);
174 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationRightToLeft;
175 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 0);
176 |
177 | point = CGPointMake(self.gliderVC.scrollView.contentSize.width, 0);
178 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationLeftToRight;
179 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 1);
180 |
181 | point = CGPointMake(0, self.gliderVC.scrollView.contentSize.width / 2.5);
182 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationBottomToTop;
183 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 0);
184 |
185 | point = CGPointMake(0, self.gliderVC.scrollView.contentSize.width / 1.4);
186 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationTopToBottom;
187 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 1);
188 | }
189 |
190 | #pragma mark - Helper methods
191 |
192 | - (id)nilReturnMethod {
193 | return nil;
194 | }
195 |
196 | - (void)glideViewController:(nonnull RDGliderViewController *)glideViewController
197 | hasChangedOffsetOfContent:(CGPoint)offset {
198 | XCTAssertNotNil(glideViewController);
199 |
200 | }
201 |
202 | - (void)glideViewControllerWillExpand:(nonnull RDGliderViewController *)glideViewController {
203 | XCTAssertNotNil(glideViewController);
204 | [_testExpWill fulfill];
205 | }
206 |
207 | - (void)glideViewControllerWillCollapse:(nonnull RDGliderViewController *)glideViewController {
208 | XCTAssertNotNil(glideViewController);
209 | [_testExpWill fulfill];
210 | }
211 |
212 | - (void)glideViewControllerDidExpand:(nonnull RDGliderViewController *)glideViewController {
213 | XCTAssertNotNil(glideViewController);
214 | [_testExpDid fulfill];
215 | }
216 |
217 | - (void)glideViewControllerDidCollapse:(nonnull RDGliderViewController *)glideViewController {
218 | XCTAssertNotNil(glideViewController);
219 | [_testExpDid fulfill];
220 | }
221 |
222 |
223 | @end
224 |
--------------------------------------------------------------------------------
/RDGliderViewController/RDGliderViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDGliderViewController.m
3 | // GliderSample
4 | //
5 | // Created by GuillermoD on 8/4/16.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import "RDGliderViewController.h"
10 |
11 | @interface RDGliderViewController ()
12 |
13 | @property (nonatomic, nonnull) RDScrollView *scrollView;
14 | @property (nonatomic, nonnull) UIViewController *contentViewController;
15 | @property (nonatomic) BOOL isObservingOffsets;
16 |
17 | @end
18 |
19 | @implementation RDGliderViewController
20 |
21 | - (instancetype)initOn:(nonnull UIViewController *)parent
22 | WithContent:(nonnull RDGliderContentViewController *)content
23 | type:(RDScrollViewOrientationType)type
24 | AndOffsets:(nonnull NSArray *)offsets {
25 | if (self = [super init]) {
26 |
27 | if (!parent ) {
28 | [NSException raise:@"Invalid parentViewController" format:@"parentViewController cannot be nil"];
29 | }
30 |
31 | [parent addChildViewController:self];
32 |
33 | _scrollView = [[RDScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(parent.view.frame), CGRectGetHeight(parent.view.frame))];
34 |
35 | [parent.view addSubview:self.scrollView];
36 |
37 | [self setContentViewController:content type:type offsets:offsets];
38 | }
39 |
40 | return self;
41 | }
42 |
43 | - (NSArray *)offsets {
44 | if (!self.scrollView) {
45 | [NSException raise:@"Invalid request" format:@"RDGliderViewController have to instantiate first on a viewController"];
46 | }
47 |
48 | return self.scrollView.offsets;
49 |
50 | }
51 |
52 | - (void)setOffsets:(NSArray *)offsets {
53 | if (offsets.count > 0) {
54 | if (self.scrollView) {
55 | [self.scrollView setOffsets:offsets];
56 | }
57 | else {
58 | [NSException raise:@"Internal Inconsistency" format:@"RDGliderViewController requires an scrollview to be instanciated."];
59 | }
60 | }
61 | else {
62 | [NSException raise:@"Invalid Offsets" format:@"Array of offsets cannot be Zero"];
63 | }
64 | }
65 |
66 | - (NSUInteger)currentOffsetIndex {
67 | if (self.scrollView) {
68 | return self.scrollView.offsetIndex;
69 | }
70 |
71 | return 0;
72 | }
73 |
74 | - (RDScrollViewOrientationType)orientationType {
75 | if (self.scrollView) {
76 | return self.scrollView.orientationType;
77 | }
78 |
79 | return RDScrollViewOrientationUnknown;
80 | }
81 |
82 | - (void)setMarginOffset:(CGFloat)marginOffset {
83 | [self.scrollView setMargin:marginOffset];
84 | }
85 |
86 | - (CGFloat)marginOffset {
87 | if (!self.scrollView) {
88 | return 0.0f;
89 | }
90 |
91 | return [self.scrollView margin];
92 | }
93 |
94 | - (NSUInteger)nearestOffsetIndexTo:(CGPoint)contentOffset {
95 | NSUInteger index = 0;
96 | CGFloat offset = contentOffset.x;
97 | CGFloat threshold = CGRectGetWidth(self.scrollView.content.frame);
98 |
99 | if (self.orientationType == RDScrollViewOrientationBottomToTop ||
100 | self.orientationType == RDScrollViewOrientationTopToBottom) {
101 | offset = contentOffset.y;
102 | threshold = CGRectGetHeight(self.scrollView.content.frame);
103 | }
104 |
105 | NSUInteger distance = INT_MAX;
106 | for (NSUInteger i = 0 ; i < [self.offsets count] ; i++) {
107 | CGFloat transformedOffset = [[self.scrollView offsets] objectAtIndex:i].floatValue * threshold;
108 | NSUInteger distToAnchor = (NSUInteger)fabs(offset - transformedOffset);
109 | if (distToAnchor < distance) {
110 | distance = distToAnchor;
111 | index = i;
112 | }
113 | }
114 |
115 | return (index == 0 && self.disableDraggingToClose) ? 1 : index;
116 | }
117 |
118 | #pragma mark - Public methods
119 |
120 | - (void)setContentViewController:(nonnull RDGliderContentViewController *)contentViewController
121 | type:(RDScrollViewOrientationType)type
122 | offsets:(nonnull NSArray *)offsets {
123 |
124 | if (!contentViewController) {
125 | [NSException raise:@"Invalid contentViewController" format:@"ViewController cannot be nil"];
126 | }
127 |
128 | if (self.scrollView && !CGRectIsNull(self.scrollView.frame)) {
129 |
130 | self.parentViewController.automaticallyAdjustsScrollViewInsets = NO;
131 | self.automaticallyAdjustsScrollViewInsets = NO;
132 |
133 | [self.scrollView setOrientationType:type];
134 | [self.scrollView setOffsets:offsets];
135 |
136 | self.contentViewController = contentViewController;
137 | [self.scrollView setContent:self.contentViewController.view];
138 |
139 | self.scrollView.delegate = self;
140 |
141 | [self close];
142 | }
143 | }
144 |
145 | - (void)shake {
146 |
147 | if (!self.scrollView) {
148 | return;
149 | }
150 |
151 | CGFloat shakeMargin = 10.0f;
152 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
153 | [animation setDuration:0.05];
154 | [animation setRepeatCount:2];
155 | [animation setAutoreverses:YES];
156 | [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
157 |
158 | if (self.orientationType == RDScrollViewOrientationRightToLeft) {
159 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x,
160 | [self.scrollView center].y)]];
161 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x + shakeMargin,
162 | [self.scrollView center].y)]];
163 | }
164 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) {
165 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x,
166 | [self.scrollView center].y)]];
167 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x - shakeMargin,
168 | [self.scrollView center].y)]];
169 | }
170 | else if (self.orientationType == RDScrollViewOrientationBottomToTop) {
171 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x,
172 | [self.scrollView center].y)]];
173 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x,
174 | [self.scrollView center].y + shakeMargin)]];
175 | }
176 | else if (self.orientationType == RDScrollViewOrientationTopToBottom) {
177 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x,
178 | [self.scrollView center].y)]];
179 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x,
180 | [self.scrollView center].y - shakeMargin)]];
181 | }
182 |
183 | [[self.scrollView layer] addAnimation:animation forKey:@"position"];
184 | }
185 |
186 | - (void)expand {
187 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillExpand:)]) {
188 | [self.delegate glideViewControllerWillExpand:self];
189 | }
190 |
191 | [self.scrollView expandWithCompletion:^(BOOL finished) {
192 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidExpand:)]) {
193 | [self.delegate glideViewControllerDidExpand:self];
194 | }
195 | }];
196 | }
197 |
198 | - (void)collapse {
199 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillCollapse:)]) {
200 | [self.delegate glideViewControllerWillCollapse:self];
201 | }
202 |
203 | [self.scrollView collapseWithCompletion:^(BOOL finished) {
204 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidCollapse:)]) {
205 | [self.delegate glideViewControllerDidCollapse:self];
206 | }
207 | }];
208 | }
209 |
210 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated {
211 | if (self.currentOffsetIndex < offsetIndex) {
212 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillExpand:)]) {
213 | [self.delegate glideViewControllerWillExpand:self];
214 | }
215 | } else if (offsetIndex < self.currentOffsetIndex) {
216 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillCollapse:)]) {
217 | [self.delegate glideViewControllerWillCollapse:self];
218 | }
219 |
220 | }
221 |
222 | [self.scrollView changeOffsetTo:offsetIndex
223 | animated:animated
224 | completion:^(BOOL finished) {
225 | if (self.currentOffsetIndex < offsetIndex) {
226 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidExpand:)]) {
227 | [self.delegate glideViewControllerDidExpand:self];
228 | }
229 | } else {
230 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidCollapse:)]) {
231 | [self.delegate glideViewControllerDidCollapse:self];
232 | }
233 | }
234 | }];
235 | }
236 |
237 | - (void)close {
238 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillCollapse:)]) {
239 | [self.delegate glideViewControllerWillCollapse:self];
240 | }
241 |
242 | [self.scrollView closeWithCompletion:^(BOOL finished) {
243 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidCollapse:)]) {
244 | [self.delegate glideViewControllerDidCollapse:self];
245 | }
246 | }];
247 | }
248 |
249 | - (BOOL)isOpen {
250 | return self.scrollView.isOpen;
251 | }
252 |
253 | #pragma mark - UIScrollView delegate
254 |
255 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
256 | if ([self.delegate respondsToSelector:@selector(glideViewController:hasChangedOffsetOfContent:)]) {
257 | [self.delegate glideViewController:self hasChangedOffsetOfContent:scrollView.contentOffset];
258 | }
259 | }
260 |
261 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
262 | [self changeOffsetTo:[self nearestOffsetIndexTo:scrollView.contentOffset]
263 | animated:NO];
264 | }
265 |
266 | - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
267 | [self.scrollView setContentOffset:self.scrollView.contentOffset animated:NO];
268 | }
269 |
270 | #pragma mark - Rotation event
271 |
272 | - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
273 | [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
274 | if (self.contentViewController) {
275 | [self.contentViewController viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
276 | }
277 |
278 | [coordinator animateAlongsideTransition:^(id _Nonnull context) {
279 | [self changeOffsetTo:self.currentOffsetIndex animated:YES];
280 | } completion:^(id _Nonnull context) {
281 | [self.scrollView recalculateContentSize];
282 | }];
283 | }
284 |
285 | @end
286 |
--------------------------------------------------------------------------------
/RDGliderViewController/RDScrollView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDScrollView.m
3 | // GliderSample
4 | //
5 | // Created by GuillermoD on 8/3/16.
6 | // Copyright © 2017. All rights reserved.
7 | //
8 |
9 | #import "RDScrollView.h"
10 |
11 | @interface RDScrollView()
12 |
13 | @property (nonatomic) NSUInteger offsetIndex;
14 | @property (nonatomic) BOOL isOpen;
15 | @property (nonatomic) NSLayoutConstraint *leftToRightLeadingContraint;
16 | @property (nonatomic) NSLayoutConstraint *topToBottomTopContraint;
17 |
18 | @end
19 |
20 | #define kDefaultMargin 20
21 |
22 | #define kAniDuration 0.3
23 | #define kAniDelay 0.0
24 | #define kAniDamping 0.7
25 | #define kAniVelocity 0.6
26 |
27 | @implementation RDScrollView
28 |
29 | - (instancetype)initWithFrame:(CGRect)frame {
30 | if (self = [super initWithFrame:frame]) {
31 | [self initializeByDefault];
32 | }
33 |
34 | return self;
35 | }
36 |
37 | - (void)recalculateContentSize {
38 |
39 | CGSize size = CGSizeZero;
40 |
41 | if (self.orientationType == RDScrollViewOrientationBottomToTop) {
42 | size.height = CGRectGetHeight(self.frame) + (CGRectGetHeight(self.content.frame) * [self.offsets lastObject].floatValue) + self.margin;
43 | }
44 | else if (self.orientationType == RDScrollViewOrientationTopToBottom){
45 | size.height = CGRectGetHeight(self.frame) + (CGRectGetHeight(self.content.frame) * [self.offsets firstObject].floatValue) + self.margin;
46 | }
47 | else if (self.orientationType == RDScrollViewOrientationRightToLeft) {
48 | size.width = CGRectGetWidth(self.frame) + (CGRectGetWidth(self.content.frame) * [self.offsets lastObject].floatValue) + self.margin;
49 | }
50 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) {
51 | size.width = CGRectGetWidth(self.frame) + (CGRectGetWidth(self.content.frame) * [self.offsets firstObject].floatValue) + self.margin;
52 | }
53 |
54 | [self setContentSize:size];
55 | [self layoutIfNeeded];
56 |
57 | dispatch_async (dispatch_get_main_queue (), ^{
58 | [self changeOffsetTo:self.offsetIndex animated:NO completion:nil];
59 | });
60 | }
61 |
62 | - (void)setMargin:(CGFloat)margin {
63 | _margin = margin;
64 |
65 | if (self.orientationType == RDScrollViewOrientationTopToBottom) {
66 | self.topToBottomTopContraint.constant = margin;
67 | }
68 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) {
69 | self.leftToRightLeadingContraint.constant = margin;
70 | }
71 |
72 | [self recalculateContentSize];
73 | }
74 |
75 | - (void)setContent:(UIView *)content {
76 | _content = content;
77 | [self addContent:_content];
78 | }
79 |
80 | - (void)setOffsets:(NSArray *)offsets {
81 | if (!offsets) {
82 | [NSException raise:@"Invalid offsets array" format:@"offsets array cannot be nil"];
83 | }
84 |
85 | NSArray *clearOffsets = [offsets valueForKeyPath:@"@distinctUnionOfObjects.self"];
86 | NSMutableArray *reversedOffsets = [NSMutableArray new];
87 |
88 | for (NSNumber *number in clearOffsets) {
89 | if ([number floatValue] > 1.0) {
90 | [NSException raise:@"Invalid offset value" format:@"offset represents a %% of contentView to be shown i.e. 0.5 of a contentView of 100px will show 50px"];
91 | }
92 | else if (self.orientationType == RDScrollViewOrientationTopToBottom ||
93 | self.orientationType == RDScrollViewOrientationLeftToRight) {
94 | [reversedOffsets addObject:@(1 - number.floatValue)];
95 | }
96 | }
97 |
98 | NSArray *newOffsets = [clearOffsets sortedArrayUsingSelector: @selector(compare:)];
99 |
100 | if (reversedOffsets.count > 0) {
101 | newOffsets = [reversedOffsets sortedArrayUsingSelector: @selector(compare:)];
102 | newOffsets = [[newOffsets reverseObjectEnumerator] allObjects];
103 | }
104 |
105 | if (newOffsets && ![_offsets isEqualToArray:newOffsets]) {
106 | [self recalculateContentSize];
107 | }
108 | _offsets = [newOffsets copy];
109 |
110 | }
111 |
112 | - (void)expandWithCompletion:(void (^)(BOOL finished))completion {
113 | NSUInteger nextIndex = (self.offsetIndex + 1 < [[self offsets] count]) ? self.offsetIndex + 1 : self.offsetIndex;
114 | [self changeOffsetTo:nextIndex animated:NO completion:completion];
115 | }
116 |
117 | - (void)collapseWithCompletion:(void (^)(BOOL finished))completion {
118 |
119 | NSUInteger nextIndex = self.offsetIndex == 0 ? 0 : self.offsetIndex - 1;
120 | [self changeOffsetTo:nextIndex animated:NO completion:completion];
121 | }
122 |
123 | - (void)closeWithCompletion:(void (^)(BOOL finished))completion {
124 | [self changeOffsetTo:0 animated:NO completion:completion];
125 | }
126 |
127 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated completion:(void (^)(BOOL finished))completion {
128 | self.panGestureRecognizer.enabled = NO;
129 | [UIView animateWithDuration:self.duration delay:self.delay usingSpringWithDamping:self.damping
130 | initialSpringVelocity:self.velocity options:UIViewAnimationOptionCurveEaseOut animations:^{
131 |
132 | [self.content setHidden:NO];
133 |
134 | if (self.orientationType == RDScrollViewOrientationLeftToRight) {
135 | CGFloat margin = (offsetIndex == 0 || offsetIndex == self.offsets.count - 1) ? self.margin : 0;
136 | [self setContentOffset:CGPointMake(([[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetWidth(self.content.frame)) + margin, self.contentOffset.y) animated:animated];
137 | }
138 | else if (self.orientationType == RDScrollViewOrientationRightToLeft) {
139 | [self setContentOffset:CGPointMake([[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetWidth(self.content.frame), self.contentOffset.y) animated:animated];
140 | }
141 | else if (self.orientationType == RDScrollViewOrientationBottomToTop) {
142 | [self setContentOffset:CGPointMake(self.contentOffset.x, [[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetHeight(self.content.frame)) animated:animated];
143 | }
144 | else if (self.orientationType == RDScrollViewOrientationTopToBottom) {
145 | CGFloat margin = (offsetIndex == 0 || offsetIndex == self.offsets.count - 1) ? self.margin : 0;
146 | [self setContentOffset:CGPointMake(self.contentOffset.x, ([[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetHeight(self.content.frame)) + margin) animated:animated];
147 | }
148 | } completion:^(BOOL finished) {
149 | self.offsetIndex = offsetIndex;
150 |
151 | if (self.orientationType == RDScrollViewOrientationLeftToRight ||
152 | self.orientationType == RDScrollViewOrientationTopToBottom) {
153 | self.isOpen = ([[self offsets] objectAtIndex:offsetIndex].floatValue == 1) ? NO : YES;
154 | } else {
155 | self.isOpen = ([[self offsets] objectAtIndex:offsetIndex].floatValue == 0) ? NO : YES;
156 | }
157 |
158 | [self.content setHidden:!self.isOpen];
159 |
160 | self.panGestureRecognizer.enabled = YES;
161 |
162 | if (completion) {
163 | completion(finished);
164 | }
165 | }];
166 | }
167 |
168 | #pragma mark - Private Methods
169 |
170 | - (BOOL)viewContainsPoint:(CGPoint)point inView:(UIView *)content {
171 | if (CGRectContainsPoint(_content.frame, point)) {
172 | return YES;
173 | }
174 |
175 | if (self.selectContentSubViews) {
176 | for (UIView *subView in content.subviews) {
177 | if (CGRectContainsPoint(subView.frame, point)) {
178 | return YES;
179 | }
180 | }
181 | }
182 |
183 | return NO;
184 | }
185 |
186 | - (void)initializeByDefault {
187 | self.margin = (CGFloat)kDefaultMargin;
188 |
189 | self.duration = (CGFloat)kAniDuration;
190 | self.delay = (CGFloat)kAniDelay;
191 | self.damping = (CGFloat)kAniDamping;
192 | self.velocity = (CGFloat)kAniVelocity;
193 |
194 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
195 |
196 | self.showsVerticalScrollIndicator = NO;
197 | self.showsHorizontalScrollIndicator = NO;
198 |
199 | [self setBounces:NO];
200 | [self setDirectionalLockEnabled:YES];
201 | [self setScrollsToTop:NO];
202 | [self setPagingEnabled:NO];
203 | [self setContentInset:UIEdgeInsetsZero];
204 | [self setDecelerationRate:UIScrollViewDecelerationRateFast];
205 |
206 | [self setOrientationType:RDScrollViewOrientationRightToLeft];
207 | }
208 |
209 | - (void)addContent:(UIView *)content {
210 | if (!content && CGRectIsNull(content.frame)) {
211 | return;
212 | }
213 |
214 | [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
215 |
216 | UIView *container = [UIView new];
217 |
218 | [container addSubview:content];
219 | [self addSubview:container];
220 |
221 | container.translatesAutoresizingMaskIntoConstraints = NO;
222 | content.translatesAutoresizingMaskIntoConstraints = NO;
223 |
224 | if (self.orientationType == RDScrollViewOrientationRightToLeft) {
225 |
226 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
227 | toItem:container attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0],
228 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
229 | toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0],
230 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual
231 | toItem:container attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0]]];
232 |
233 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
234 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0],
235 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
236 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0],
237 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
238 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]]];
239 | if (CGRectIsEmpty(_content.frame)) {
240 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
241 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0],
242 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
243 | toItem:self attribute:NSLayoutAttributeWidth multiplier:2.0 constant:0.0]]];
244 | } else {
245 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
246 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetWidth(content.frame)]];
247 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
248 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:CGRectGetWidth(content.frame)]];
249 | }
250 | }
251 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) {
252 |
253 | self.leftToRightLeadingContraint = [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
254 | toItem:container attribute:NSLayoutAttributeLeading multiplier:1.0 constant:self.margin];
255 |
256 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
257 | toItem:container attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0],
258 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
259 | toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0], self.leftToRightLeadingContraint]];
260 |
261 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
262 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0],
263 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
264 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0],
265 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
266 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]]];
267 | if (CGRectIsEmpty(_content.frame)) {
268 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
269 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0],
270 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
271 | toItem:self attribute:NSLayoutAttributeWidth multiplier:2.0 constant:0.0]]];
272 | } else {
273 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
274 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetWidth(content.frame)]];
275 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
276 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:CGRectGetWidth(content.frame)]];
277 | }
278 | }
279 | else if (self.orientationType == RDScrollViewOrientationBottomToTop) {
280 |
281 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
282 | toItem:container attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0],
283 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual
284 | toItem:container attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0],
285 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
286 | toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]]];
287 |
288 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
289 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0],
290 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
291 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0],
292 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
293 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]]];
294 |
295 | if (CGRectIsEmpty(_content.frame)) {
296 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
297 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0],
298 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
299 | toItem:self attribute:NSLayoutAttributeHeight multiplier:2.0 constant:0.0]]];
300 | } else {
301 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
302 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetHeight(content.frame)]];
303 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
304 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:CGRectGetHeight(content.frame)]];
305 | }
306 | }
307 | else if (self.orientationType == RDScrollViewOrientationTopToBottom) {
308 |
309 | self.topToBottomTopContraint = [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
310 | toItem:container attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.margin];
311 |
312 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
313 | toItem:container attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0],
314 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual
315 | toItem:container attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0], self.topToBottomTopContraint]];
316 |
317 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
318 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0],
319 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
320 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0],
321 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
322 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]]];
323 |
324 | if (CGRectIsEmpty(_content.frame)) {
325 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
326 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0],
327 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
328 | toItem:self attribute:NSLayoutAttributeHeight multiplier:2.0 constant:0.0]]];
329 | } else {
330 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
331 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetHeight(content.frame)]];
332 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
333 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:CGRectGetHeight(content.frame)]];
334 | }
335 | }
336 |
337 | [self layoutIfNeeded];
338 | [self recalculateContentSize];
339 | }
340 |
341 | #pragma mark - touch handler
342 |
343 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
344 | if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
345 | return nil;
346 | }
347 |
348 | if (_content && [self viewContainsPoint:point inView:_content]) {
349 |
350 | for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
351 |
352 | CGPoint pt = CGPointMake((CGFloat)fabs(point.x), (CGFloat)fabs(point.y));
353 |
354 | CGPoint convertedPoint = [subview convertPoint:pt fromView:self];
355 | UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
356 |
357 | if (hitTestView) {
358 | return hitTestView;
359 | }
360 | }
361 | }
362 |
363 | return nil;
364 | }
365 |
366 | @end
367 |
--------------------------------------------------------------------------------
/Example/GliderSample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
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 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
141 |
154 |
166 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
--------------------------------------------------------------------------------
/Example/GliderSample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C7174C321EAA0AEC000A32F3 /* RDGliderContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C7174C311EAA0AEC000A32F3 /* RDGliderContentViewController.m */; };
11 | C78A83011EA1041C008F5878 /* RDGliderViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C78A83001EA1041C008F5878 /* RDGliderViewControllerTests.m */; };
12 | C79985461E92332900B80DA5 /* RDButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C79985451E92332900B80DA5 /* RDButton.m */; };
13 | C799854D1E9246BA00B80DA5 /* RDGliderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C799854A1E9246BA00B80DA5 /* RDGliderViewController.m */; };
14 | C79985511E92480D00B80DA5 /* RDScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = C79985501E92480D00B80DA5 /* RDScrollView.m */; };
15 | C79DCBE51E925EE000E1EF1A /* ContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C79DCBE31E925EE000E1EF1A /* ContentViewController.m */; };
16 | C79DCBE61E925EE000E1EF1A /* ContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C79DCBE41E925EE000E1EF1A /* ContentViewController.xib */; };
17 | C79DCBE91E92677700E1EF1A /* RDGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = C79DCBE81E92677700E1EF1A /* RDGradientView.m */; };
18 | C7D9C61E1E9220A000F80609 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C61D1E9220A000F80609 /* main.m */; };
19 | C7D9C6211E9220A000F80609 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C6201E9220A000F80609 /* AppDelegate.m */; };
20 | C7D9C6241E9220A000F80609 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C6231E9220A000F80609 /* ViewController.m */; };
21 | C7D9C6271E9220A000F80609 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7D9C6251E9220A000F80609 /* Main.storyboard */; };
22 | C7D9C6291E9220A000F80609 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7D9C6281E9220A000F80609 /* Assets.xcassets */; };
23 | C7D9C62C1E9220A000F80609 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7D9C62A1E9220A000F80609 /* LaunchScreen.storyboard */; };
24 | C7D9C6371E9220A000F80609 /* RDScrollViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C6361E9220A000F80609 /* RDScrollViewTests.m */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXContainerItemProxy section */
28 | C7D9C6331E9220A000F80609 /* PBXContainerItemProxy */ = {
29 | isa = PBXContainerItemProxy;
30 | containerPortal = C7D9C6111E9220A000F80609 /* Project object */;
31 | proxyType = 1;
32 | remoteGlobalIDString = C7D9C6181E9220A000F80609;
33 | remoteInfo = GliderSample;
34 | };
35 | /* End PBXContainerItemProxy section */
36 |
37 | /* Begin PBXFileReference section */
38 | C7174C301EAA0AEC000A32F3 /* RDGliderContentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDGliderContentViewController.h; sourceTree = ""; };
39 | C7174C311EAA0AEC000A32F3 /* RDGliderContentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDGliderContentViewController.m; sourceTree = ""; };
40 | C78A83001EA1041C008F5878 /* RDGliderViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDGliderViewControllerTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
41 | C79985441E92332900B80DA5 /* RDButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RDButton.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
42 | C79985451E92332900B80DA5 /* RDButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDButton.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
43 | C79985491E9246BA00B80DA5 /* RDGliderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RDGliderViewController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
44 | C799854A1E9246BA00B80DA5 /* RDGliderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDGliderViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
45 | C799854F1E92480D00B80DA5 /* RDScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDScrollView.h; sourceTree = ""; };
46 | C79985501E92480D00B80DA5 /* RDScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDScrollView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
47 | C79DCBE21E925EE000E1EF1A /* ContentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentViewController.h; sourceTree = ""; };
48 | C79DCBE31E925EE000E1EF1A /* ContentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContentViewController.m; sourceTree = ""; };
49 | C79DCBE41E925EE000E1EF1A /* ContentViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContentViewController.xib; sourceTree = ""; };
50 | C79DCBE71E92677700E1EF1A /* RDGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RDGradientView.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
51 | C79DCBE81E92677700E1EF1A /* RDGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDGradientView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
52 | C7D9C6191E9220A000F80609 /* GliderSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GliderSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
53 | C7D9C61D1E9220A000F80609 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = main.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
54 | C7D9C61F1E9220A000F80609 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
55 | C7D9C6201E9220A000F80609 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
56 | C7D9C6221E9220A000F80609 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
57 | C7D9C6231E9220A000F80609 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
58 | C7D9C6261E9220A000F80609 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
59 | C7D9C6281E9220A000F80609 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
60 | C7D9C62B1E9220A000F80609 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
61 | C7D9C62D1E9220A000F80609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
62 | C7D9C6321E9220A000F80609 /* GliderSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GliderSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63 | C7D9C6361E9220A000F80609 /* RDScrollViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDScrollViewTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
64 | C7D9C6381E9220A000F80609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
65 | /* End PBXFileReference section */
66 |
67 | /* Begin PBXFrameworksBuildPhase section */
68 | C7D9C6161E9220A000F80609 /* Frameworks */ = {
69 | isa = PBXFrameworksBuildPhase;
70 | buildActionMask = 2147483647;
71 | files = (
72 | );
73 | runOnlyForDeploymentPostprocessing = 0;
74 | };
75 | C7D9C62F1E9220A000F80609 /* Frameworks */ = {
76 | isa = PBXFrameworksBuildPhase;
77 | buildActionMask = 2147483647;
78 | files = (
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | /* End PBXFrameworksBuildPhase section */
83 |
84 | /* Begin PBXGroup section */
85 | C79985471E92462500B80DA5 /* Helpers */ = {
86 | isa = PBXGroup;
87 | children = (
88 | C79985441E92332900B80DA5 /* RDButton.h */,
89 | C79985451E92332900B80DA5 /* RDButton.m */,
90 | C79DCBE71E92677700E1EF1A /* RDGradientView.h */,
91 | C79DCBE81E92677700E1EF1A /* RDGradientView.m */,
92 | );
93 | path = Helpers;
94 | sourceTree = "";
95 | };
96 | C79985481E92469300B80DA5 /* RDGliderViewController */ = {
97 | isa = PBXGroup;
98 | children = (
99 | C7174C301EAA0AEC000A32F3 /* RDGliderContentViewController.h */,
100 | C7174C311EAA0AEC000A32F3 /* RDGliderContentViewController.m */,
101 | C79985491E9246BA00B80DA5 /* RDGliderViewController.h */,
102 | C799854A1E9246BA00B80DA5 /* RDGliderViewController.m */,
103 | C799854F1E92480D00B80DA5 /* RDScrollView.h */,
104 | C79985501E92480D00B80DA5 /* RDScrollView.m */,
105 | );
106 | name = RDGliderViewController;
107 | path = ../../RDGliderViewController;
108 | sourceTree = "";
109 | };
110 | C7D9C6101E9220A000F80609 = {
111 | isa = PBXGroup;
112 | children = (
113 | C7D9C61B1E9220A000F80609 /* GliderSample */,
114 | C7D9C6351E9220A000F80609 /* GliderSampleTests */,
115 | C7D9C61A1E9220A000F80609 /* Products */,
116 | );
117 | sourceTree = "";
118 | };
119 | C7D9C61A1E9220A000F80609 /* Products */ = {
120 | isa = PBXGroup;
121 | children = (
122 | C7D9C6191E9220A000F80609 /* GliderSample.app */,
123 | C7D9C6321E9220A000F80609 /* GliderSampleTests.xctest */,
124 | );
125 | name = Products;
126 | sourceTree = "";
127 | };
128 | C7D9C61B1E9220A000F80609 /* GliderSample */ = {
129 | isa = PBXGroup;
130 | children = (
131 | C79985481E92469300B80DA5 /* RDGliderViewController */,
132 | C7D9C61F1E9220A000F80609 /* AppDelegate.h */,
133 | C7D9C6201E9220A000F80609 /* AppDelegate.m */,
134 | C7D9C6221E9220A000F80609 /* ViewController.h */,
135 | C7D9C6231E9220A000F80609 /* ViewController.m */,
136 | C79DCBE21E925EE000E1EF1A /* ContentViewController.h */,
137 | C79DCBE31E925EE000E1EF1A /* ContentViewController.m */,
138 | C79DCBE41E925EE000E1EF1A /* ContentViewController.xib */,
139 | C7D9C62A1E9220A000F80609 /* LaunchScreen.storyboard */,
140 | C7D9C6251E9220A000F80609 /* Main.storyboard */,
141 | C79985471E92462500B80DA5 /* Helpers */,
142 | C7D9C61C1E9220A000F80609 /* Supporting Files */,
143 | );
144 | path = GliderSample;
145 | sourceTree = "";
146 | };
147 | C7D9C61C1E9220A000F80609 /* Supporting Files */ = {
148 | isa = PBXGroup;
149 | children = (
150 | C7D9C6281E9220A000F80609 /* Assets.xcassets */,
151 | C7D9C62D1E9220A000F80609 /* Info.plist */,
152 | C7D9C61D1E9220A000F80609 /* main.m */,
153 | );
154 | name = "Supporting Files";
155 | sourceTree = "";
156 | };
157 | C7D9C6351E9220A000F80609 /* GliderSampleTests */ = {
158 | isa = PBXGroup;
159 | children = (
160 | C7D9C6361E9220A000F80609 /* RDScrollViewTests.m */,
161 | C7D9C6381E9220A000F80609 /* Info.plist */,
162 | C78A83001EA1041C008F5878 /* RDGliderViewControllerTests.m */,
163 | );
164 | path = GliderSampleTests;
165 | sourceTree = "";
166 | };
167 | /* End PBXGroup section */
168 |
169 | /* Begin PBXNativeTarget section */
170 | C7D9C6181E9220A000F80609 /* GliderSample */ = {
171 | isa = PBXNativeTarget;
172 | buildConfigurationList = C7D9C63B1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSample" */;
173 | buildPhases = (
174 | C7D9C6151E9220A000F80609 /* Sources */,
175 | C7D9C6161E9220A000F80609 /* Frameworks */,
176 | C7D9C6171E9220A000F80609 /* Resources */,
177 | C77AEA7B1E9E0AE700A1C586 /* Build number */,
178 | C77AEA7A1E9E099F00A1C586 /* CHANGELOG */,
179 | );
180 | buildRules = (
181 | );
182 | dependencies = (
183 | );
184 | name = GliderSample;
185 | productName = GliderSample;
186 | productReference = C7D9C6191E9220A000F80609 /* GliderSample.app */;
187 | productType = "com.apple.product-type.application";
188 | };
189 | C7D9C6311E9220A000F80609 /* GliderSampleTests */ = {
190 | isa = PBXNativeTarget;
191 | buildConfigurationList = C7D9C63E1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSampleTests" */;
192 | buildPhases = (
193 | C7D9C62E1E9220A000F80609 /* Sources */,
194 | C7D9C62F1E9220A000F80609 /* Frameworks */,
195 | C7D9C6301E9220A000F80609 /* Resources */,
196 | );
197 | buildRules = (
198 | );
199 | dependencies = (
200 | C7D9C6341E9220A000F80609 /* PBXTargetDependency */,
201 | );
202 | name = GliderSampleTests;
203 | productName = GliderSampleTests;
204 | productReference = C7D9C6321E9220A000F80609 /* GliderSampleTests.xctest */;
205 | productType = "com.apple.product-type.bundle.unit-test";
206 | };
207 | /* End PBXNativeTarget section */
208 |
209 | /* Begin PBXProject section */
210 | C7D9C6111E9220A000F80609 /* Project object */ = {
211 | isa = PBXProject;
212 | attributes = {
213 | CLASSPREFIX = RD;
214 | LastUpgradeCheck = 0820;
215 | ORGANIZATIONNAME = "Guillermo Delgado";
216 | TargetAttributes = {
217 | C7D9C6181E9220A000F80609 = {
218 | CreatedOnToolsVersion = 8.2.1;
219 | ProvisioningStyle = Automatic;
220 | };
221 | C7D9C6311E9220A000F80609 = {
222 | CreatedOnToolsVersion = 8.2.1;
223 | DevelopmentTeam = RT5Y8QLUYK;
224 | ProvisioningStyle = Automatic;
225 | TestTargetID = C7D9C6181E9220A000F80609;
226 | };
227 | };
228 | };
229 | buildConfigurationList = C7D9C6141E9220A000F80609 /* Build configuration list for PBXProject "GliderSample" */;
230 | compatibilityVersion = "Xcode 3.2";
231 | developmentRegion = English;
232 | hasScannedForEncodings = 0;
233 | knownRegions = (
234 | en,
235 | Base,
236 | );
237 | mainGroup = C7D9C6101E9220A000F80609;
238 | productRefGroup = C7D9C61A1E9220A000F80609 /* Products */;
239 | projectDirPath = "";
240 | projectRoot = "";
241 | targets = (
242 | C7D9C6181E9220A000F80609 /* GliderSample */,
243 | C7D9C6311E9220A000F80609 /* GliderSampleTests */,
244 | );
245 | };
246 | /* End PBXProject section */
247 |
248 | /* Begin PBXResourcesBuildPhase section */
249 | C7D9C6171E9220A000F80609 /* Resources */ = {
250 | isa = PBXResourcesBuildPhase;
251 | buildActionMask = 2147483647;
252 | files = (
253 | C79DCBE61E925EE000E1EF1A /* ContentViewController.xib in Resources */,
254 | C7D9C62C1E9220A000F80609 /* LaunchScreen.storyboard in Resources */,
255 | C7D9C6291E9220A000F80609 /* Assets.xcassets in Resources */,
256 | C7D9C6271E9220A000F80609 /* Main.storyboard in Resources */,
257 | );
258 | runOnlyForDeploymentPostprocessing = 0;
259 | };
260 | C7D9C6301E9220A000F80609 /* Resources */ = {
261 | isa = PBXResourcesBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | );
265 | runOnlyForDeploymentPostprocessing = 0;
266 | };
267 | /* End PBXResourcesBuildPhase section */
268 |
269 | /* Begin PBXShellScriptBuildPhase section */
270 | C77AEA7A1E9E099F00A1C586 /* CHANGELOG */ = {
271 | isa = PBXShellScriptBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | );
275 | inputPaths = (
276 | );
277 | name = CHANGELOG;
278 | outputPaths = (
279 | );
280 | runOnlyForDeploymentPostprocessing = 0;
281 | shellPath = /bin/sh;
282 | shellScript = "FILE='../CHANGELOG.md'\nbranch=$(git branch | sed -n -e 's/^\\* \\(.*\\)/\\1/p')\nLATEST_TAG=$(git describe --abbrev=0)\n\nBUILD_NUM=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nBUILD_VER=$(/usr/libexec/Plistbuddy -c \"Print CFBundleShortVersionString\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\n\n> $FILE\necho '## CHANGELOG' >> $FILE\necho '' >> $FILE\necho '#### Latest version - ' $BUILD_VER.$BUILD_NUM >> $FILE\ngit log $LATEST_TAG..HEAD --no-merges --grep=\"^\\[\" --pretty=format:\"+ %ad *%s*\" -50 --date=short | sed '/^\\s*$/d' >> $FILE\necho '' >> $FILE\necho '#### Previously released:' >> $FILE\ngit log INIT..$LATEST_TAG --no-merges --grep=\"^\\[\" --pretty=format:\"+ %ad *%s*\" -50 --date=short | sed '/^\\s*$/d' >> $FILE";
283 | };
284 | C77AEA7B1E9E0AE700A1C586 /* Build number */ = {
285 | isa = PBXShellScriptBuildPhase;
286 | buildActionMask = 2147483647;
287 | files = (
288 | );
289 | inputPaths = (
290 | );
291 | name = "Build number";
292 | outputPaths = (
293 | );
294 | runOnlyForDeploymentPostprocessing = 0;
295 | shellPath = /bin/sh;
296 | shellScript = "#Update build number with number of git commits if in release mode\nbuildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"";
297 | };
298 | /* End PBXShellScriptBuildPhase section */
299 |
300 | /* Begin PBXSourcesBuildPhase section */
301 | C7D9C6151E9220A000F80609 /* Sources */ = {
302 | isa = PBXSourcesBuildPhase;
303 | buildActionMask = 2147483647;
304 | files = (
305 | C79985511E92480D00B80DA5 /* RDScrollView.m in Sources */,
306 | C79DCBE91E92677700E1EF1A /* RDGradientView.m in Sources */,
307 | C7D9C6241E9220A000F80609 /* ViewController.m in Sources */,
308 | C799854D1E9246BA00B80DA5 /* RDGliderViewController.m in Sources */,
309 | C79DCBE51E925EE000E1EF1A /* ContentViewController.m in Sources */,
310 | C79985461E92332900B80DA5 /* RDButton.m in Sources */,
311 | C7174C321EAA0AEC000A32F3 /* RDGliderContentViewController.m in Sources */,
312 | C7D9C6211E9220A000F80609 /* AppDelegate.m in Sources */,
313 | C7D9C61E1E9220A000F80609 /* main.m in Sources */,
314 | );
315 | runOnlyForDeploymentPostprocessing = 0;
316 | };
317 | C7D9C62E1E9220A000F80609 /* Sources */ = {
318 | isa = PBXSourcesBuildPhase;
319 | buildActionMask = 2147483647;
320 | files = (
321 | C78A83011EA1041C008F5878 /* RDGliderViewControllerTests.m in Sources */,
322 | C7D9C6371E9220A000F80609 /* RDScrollViewTests.m in Sources */,
323 | );
324 | runOnlyForDeploymentPostprocessing = 0;
325 | };
326 | /* End PBXSourcesBuildPhase section */
327 |
328 | /* Begin PBXTargetDependency section */
329 | C7D9C6341E9220A000F80609 /* PBXTargetDependency */ = {
330 | isa = PBXTargetDependency;
331 | target = C7D9C6181E9220A000F80609 /* GliderSample */;
332 | targetProxy = C7D9C6331E9220A000F80609 /* PBXContainerItemProxy */;
333 | };
334 | /* End PBXTargetDependency section */
335 |
336 | /* Begin PBXVariantGroup section */
337 | C7D9C6251E9220A000F80609 /* Main.storyboard */ = {
338 | isa = PBXVariantGroup;
339 | children = (
340 | C7D9C6261E9220A000F80609 /* Base */,
341 | );
342 | name = Main.storyboard;
343 | sourceTree = "";
344 | };
345 | C7D9C62A1E9220A000F80609 /* LaunchScreen.storyboard */ = {
346 | isa = PBXVariantGroup;
347 | children = (
348 | C7D9C62B1E9220A000F80609 /* Base */,
349 | );
350 | name = LaunchScreen.storyboard;
351 | sourceTree = "";
352 | };
353 | /* End PBXVariantGroup section */
354 |
355 | /* Begin XCBuildConfiguration section */
356 | C7D9C6391E9220A000F80609 /* Debug */ = {
357 | isa = XCBuildConfiguration;
358 | buildSettings = {
359 | ALWAYS_SEARCH_USER_PATHS = NO;
360 | CLANG_ANALYZER_NONNULL = YES;
361 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
362 | CLANG_CXX_LIBRARY = "libc++";
363 | CLANG_ENABLE_MODULES = YES;
364 | CLANG_ENABLE_OBJC_ARC = YES;
365 | CLANG_WARN_BOOL_CONVERSION = YES;
366 | CLANG_WARN_CONSTANT_CONVERSION = YES;
367 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
368 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
369 | CLANG_WARN_EMPTY_BODY = YES;
370 | CLANG_WARN_ENUM_CONVERSION = YES;
371 | CLANG_WARN_INFINITE_RECURSION = YES;
372 | CLANG_WARN_INT_CONVERSION = YES;
373 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
374 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
375 | CLANG_WARN_UNREACHABLE_CODE = YES;
376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
378 | COPY_PHASE_STRIP = NO;
379 | DEBUG_INFORMATION_FORMAT = dwarf;
380 | ENABLE_STRICT_OBJC_MSGSEND = YES;
381 | ENABLE_TESTABILITY = YES;
382 | GCC_C_LANGUAGE_STANDARD = gnu99;
383 | GCC_DYNAMIC_NO_PIC = NO;
384 | GCC_NO_COMMON_BLOCKS = YES;
385 | GCC_OPTIMIZATION_LEVEL = 0;
386 | GCC_PREPROCESSOR_DEFINITIONS = (
387 | "DEBUG=1",
388 | "$(inherited)",
389 | );
390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
392 | GCC_WARN_UNDECLARED_SELECTOR = YES;
393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
394 | GCC_WARN_UNUSED_FUNCTION = YES;
395 | GCC_WARN_UNUSED_VARIABLE = YES;
396 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
397 | MTL_ENABLE_DEBUG_INFO = YES;
398 | ONLY_ACTIVE_ARCH = YES;
399 | SDKROOT = iphoneos;
400 | TARGETED_DEVICE_FAMILY = "1,2";
401 | };
402 | name = Debug;
403 | };
404 | C7D9C63A1E9220A000F80609 /* Release */ = {
405 | isa = XCBuildConfiguration;
406 | buildSettings = {
407 | ALWAYS_SEARCH_USER_PATHS = NO;
408 | CLANG_ANALYZER_NONNULL = YES;
409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
410 | CLANG_CXX_LIBRARY = "libc++";
411 | CLANG_ENABLE_MODULES = YES;
412 | CLANG_ENABLE_OBJC_ARC = YES;
413 | CLANG_WARN_BOOL_CONVERSION = YES;
414 | CLANG_WARN_CONSTANT_CONVERSION = YES;
415 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
416 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
417 | CLANG_WARN_EMPTY_BODY = YES;
418 | CLANG_WARN_ENUM_CONVERSION = YES;
419 | CLANG_WARN_INFINITE_RECURSION = YES;
420 | CLANG_WARN_INT_CONVERSION = YES;
421 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
422 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
423 | CLANG_WARN_UNREACHABLE_CODE = YES;
424 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
425 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
426 | COPY_PHASE_STRIP = NO;
427 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
428 | ENABLE_NS_ASSERTIONS = NO;
429 | ENABLE_STRICT_OBJC_MSGSEND = YES;
430 | GCC_C_LANGUAGE_STANDARD = gnu99;
431 | GCC_NO_COMMON_BLOCKS = YES;
432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
434 | GCC_WARN_UNDECLARED_SELECTOR = YES;
435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
436 | GCC_WARN_UNUSED_FUNCTION = YES;
437 | GCC_WARN_UNUSED_VARIABLE = YES;
438 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
439 | MTL_ENABLE_DEBUG_INFO = NO;
440 | SDKROOT = iphoneos;
441 | TARGETED_DEVICE_FAMILY = "1,2";
442 | VALIDATE_PRODUCT = YES;
443 | };
444 | name = Release;
445 | };
446 | C7D9C63C1E9220A000F80609 /* Debug */ = {
447 | isa = XCBuildConfiguration;
448 | buildSettings = {
449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
450 | CLANG_WARN_ASSIGN_ENUM = YES;
451 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
452 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
453 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES;
454 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
455 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
456 | CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO;
457 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
458 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
459 | CODE_SIGN_IDENTITY = "iPhone Developer";
460 | DEVELOPMENT_TEAM = "";
461 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
462 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
463 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
464 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
465 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
466 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
467 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
468 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
469 | GCC_WARN_SHADOW = YES;
470 | GCC_WARN_SIGN_COMPARE = YES;
471 | GCC_WARN_STRICT_SELECTOR_MATCH = YES;
472 | GCC_WARN_UNKNOWN_PRAGMAS = YES;
473 | GCC_WARN_UNUSED_LABEL = YES;
474 | GCC_WARN_UNUSED_PARAMETER = NO;
475 | INFOPLIST_FILE = GliderSample/Info.plist;
476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
477 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSample;
478 | PRODUCT_NAME = "$(TARGET_NAME)";
479 | };
480 | name = Debug;
481 | };
482 | C7D9C63D1E9220A000F80609 /* Release */ = {
483 | isa = XCBuildConfiguration;
484 | buildSettings = {
485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
486 | CLANG_WARN_ASSIGN_ENUM = YES;
487 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
488 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
489 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES;
490 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
491 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
492 | CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO;
493 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
494 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
495 | CODE_SIGN_IDENTITY = "iPhone Developer";
496 | DEVELOPMENT_TEAM = "";
497 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
498 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
499 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
500 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
501 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
502 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
503 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
504 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
505 | GCC_WARN_SHADOW = YES;
506 | GCC_WARN_SIGN_COMPARE = YES;
507 | GCC_WARN_STRICT_SELECTOR_MATCH = YES;
508 | GCC_WARN_UNKNOWN_PRAGMAS = YES;
509 | GCC_WARN_UNUSED_LABEL = YES;
510 | GCC_WARN_UNUSED_PARAMETER = NO;
511 | INFOPLIST_FILE = GliderSample/Info.plist;
512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
513 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSample;
514 | PRODUCT_NAME = "$(TARGET_NAME)";
515 | };
516 | name = Release;
517 | };
518 | C7D9C63F1E9220A000F80609 /* Debug */ = {
519 | isa = XCBuildConfiguration;
520 | buildSettings = {
521 | BUNDLE_LOADER = "$(TEST_HOST)";
522 | DEVELOPMENT_TEAM = RT5Y8QLUYK;
523 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
524 | INFOPLIST_FILE = GliderSampleTests/Info.plist;
525 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
526 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSampleTests;
527 | PRODUCT_NAME = "$(TARGET_NAME)";
528 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GliderSample.app/GliderSample";
529 | };
530 | name = Debug;
531 | };
532 | C7D9C6401E9220A000F80609 /* Release */ = {
533 | isa = XCBuildConfiguration;
534 | buildSettings = {
535 | BUNDLE_LOADER = "$(TEST_HOST)";
536 | DEVELOPMENT_TEAM = RT5Y8QLUYK;
537 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
538 | INFOPLIST_FILE = GliderSampleTests/Info.plist;
539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
540 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSampleTests;
541 | PRODUCT_NAME = "$(TARGET_NAME)";
542 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GliderSample.app/GliderSample";
543 | };
544 | name = Release;
545 | };
546 | /* End XCBuildConfiguration section */
547 |
548 | /* Begin XCConfigurationList section */
549 | C7D9C6141E9220A000F80609 /* Build configuration list for PBXProject "GliderSample" */ = {
550 | isa = XCConfigurationList;
551 | buildConfigurations = (
552 | C7D9C6391E9220A000F80609 /* Debug */,
553 | C7D9C63A1E9220A000F80609 /* Release */,
554 | );
555 | defaultConfigurationIsVisible = 0;
556 | defaultConfigurationName = Release;
557 | };
558 | C7D9C63B1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSample" */ = {
559 | isa = XCConfigurationList;
560 | buildConfigurations = (
561 | C7D9C63C1E9220A000F80609 /* Debug */,
562 | C7D9C63D1E9220A000F80609 /* Release */,
563 | );
564 | defaultConfigurationIsVisible = 0;
565 | defaultConfigurationName = Release;
566 | };
567 | C7D9C63E1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSampleTests" */ = {
568 | isa = XCConfigurationList;
569 | buildConfigurations = (
570 | C7D9C63F1E9220A000F80609 /* Debug */,
571 | C7D9C6401E9220A000F80609 /* Release */,
572 | );
573 | defaultConfigurationIsVisible = 0;
574 | defaultConfigurationName = Release;
575 | };
576 | /* End XCConfigurationList section */
577 | };
578 | rootObject = C7D9C6111E9220A000F80609 /* Project object */;
579 | }
580 |
--------------------------------------------------------------------------------