├── codecov.yml ├── .jazzy.yaml ├── examples ├── SVDMultiplexer.playground │ ├── contents.xcplayground │ ├── timeline.xctimeline │ └── Contents.swift ├── app │ ├── ScrollViewDelegateMultiplexerExample.xcworkspace │ │ └── contents.xcworkspacedata │ ├── ScrollViewDelegateMultiplexerExample │ │ ├── main.m │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ └── LaunchScreen.storyboard │ ├── ScrollViewDelegateMultiplexer │ │ ├── Info.plist │ │ └── ScrollViewDelegateMultiplexer.h │ └── ScrollViewDelegateMultiplexerExample.xcodeproj │ │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── ScrollViewDelegateMultiplexerExample.xcscheme │ │ └── project.pbxproj ├── ObservingPageControl.h ├── SVDMTypicalUseViewController.h ├── ObservingPageControl.m └── SVDMTypicalUseViewController.m ├── .clang-format ├── MDFScrollViewDelegateMultiplexer.podspec ├── tests └── unit │ ├── resources │ └── Info.plist │ └── ScrollViewDelegateMultiplexerExampleTests.m ├── .travis.yml ├── CHANGELOG.md ├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── src ├── MDFScrollViewDelegateMultiplexer.h └── MDFScrollViewDelegateMultiplexer.m └── LICENSE /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - tests/* 4 | - examples/* 5 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: MDFScrollViewDelegateMultiplexer 2 | umbrella_header: src/MDFScrollViewDelegateMultiplexer.h 3 | objc: true 4 | sdk: iphonesimulator 5 | -------------------------------------------------------------------------------- /examples/SVDMultiplexer.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | 3 | AllowShortFunctionsOnASingleLine: Inline 4 | AllowShortIfStatementsOnASingleLine: false 5 | AllowShortLoopsOnASingleLine: false 6 | AlwaysBreakBeforeMultilineStrings: false 7 | BinPackParameters: false 8 | ColumnLimit: 0 9 | ObjCSpaceBeforeProtocolList: true 10 | PointerBindsToType: false 11 | IndentWrappedFunctionNames: true 12 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ScrollViewDelegateMultiplexerExample 4 | // 5 | // Created by Jeff Verkoeyen on 4/5/16. 6 | // Copyright © 2016 Google. 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 | -------------------------------------------------------------------------------- /MDFScrollViewDelegateMultiplexer.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "MDFScrollViewDelegateMultiplexer" 3 | s.version = "2.0.1" 4 | s.authors = { 'Chris Cox' => 'cjcox@google.com' } 5 | s.summary = "A proxy object for UIScrollViewDelegate that forwards all received events to an ordered list of registered observers." 6 | s.homepage = "https://github.com/material-foundation/material-scrollview-delegate-multiplexer-ios" 7 | s.license = 'Apache 2.0' 8 | s.source = { :git => "https://github.com/material-foundation/material-scrollview-delegate-multiplexer-ios.git", :tag => s.version.to_s } 9 | s.platform = :ios, '9.0' 10 | s.requires_arc = true 11 | s.public_header_files = 'src/*.h' 12 | s.source_files = 'src/*.{h,m}' 13 | end 14 | -------------------------------------------------------------------------------- /examples/ObservingPageControl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | @interface ObservingPageControl : UIPageControl 20 | @end 21 | -------------------------------------------------------------------------------- /examples/SVDMTypicalUseViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | @interface SVDMTypicalUseViewController : UIViewController 20 | @end 21 | -------------------------------------------------------------------------------- /tests/unit/resources/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | @interface AppDelegate : UIResponder 20 | @property (strong, nonatomic) UIWindow *window; 21 | @end 22 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexer/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.2 3 | sudo: false 4 | notifications: 5 | email: false 6 | env: 7 | global: 8 | - LC_CTYPE=en_US.UTF-8 9 | - LANG=en_US.UTF-8 10 | matrix: 11 | - DESTINATION="OS=9.2,name=iPhone 6 Plus" SDK=iphonesimulator9.2 12 | - DESTINATION="OS=9.2,name=iPhone 6s" SDK=iphonesimulator9.2 13 | - DESTINATION="OS=9.0,name=iPhone 6 Plus" SDK=iphonesimulator9.2 14 | before_install: 15 | - gem install cocoapods --no-rdoc --no-ri --no-document --quiet 16 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 17 | - git submodule update 18 | script: 19 | - set -o pipefail 20 | - xcodebuild -workspace examples/app/ScrollViewDelegateMultiplexerExample.xcworkspace -scheme ScrollViewDelegateMultiplexerExample -sdk "$SDK" -destination "$DESTINATION" -configuration Debug GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES ONLY_ACTIVE_ARCH=NO build test | xcpretty -c; 21 | after_success: 22 | - bash <(curl -s https://codecov.io/bash) 23 | -------------------------------------------------------------------------------- /examples/ObservingPageControl.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #if !defined(__has_feature) || !__has_feature(objc_arc) 18 | #error "This file requires ARC support." 19 | #endif 20 | 21 | #import "ObservingPageControl.h" 22 | 23 | @implementation ObservingPageControl 24 | 25 | #pragma mark - UIScrollViewDelegate 26 | 27 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 28 | self.currentPage = scrollView.contentOffset.x / scrollView.bounds.size.width + 0.5; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexer/ScrollViewDelegateMultiplexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | #import "MDFScrollViewDelegateMultiplexer.h" 20 | 21 | //! Project version number for ScrollViewDelegateMultiplexer. 22 | FOUNDATION_EXPORT double ScrollViewDelegateMultiplexerVersionNumber; 23 | 24 | //! Project version string for ScrollViewDelegateMultiplexer. 25 | FOUNDATION_EXPORT const unsigned char ScrollViewDelegateMultiplexerVersionString[]; 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.1 2 | 3 | ##### Enhancements 4 | 5 | * Updates podspec. 6 | * [Adds code coverage](https://github.com/material-foundation/material-scrollview-delegate-multiplexer-ios/issues/10). ([Sean O'Shea](https://github.com/seanoshea)) 7 | 8 | ## 2.0.0 9 | 10 | ##### Breaking 11 | 12 | * Renames all GOS prefixes to MDF 13 | 14 | ##### Bug Fixes 15 | 16 | * Fixed test removeObserveringDelegate so it does not skip over indices 17 | 18 | ##### Enhancements 19 | 20 | * Updates to readme. 21 | * Corrects typos. 22 | 23 | ## 1.0.1 24 | 25 | ##### Enhancements 26 | 27 | * Updates readme combiner example. 28 | * Adds example using the combiner protocol. 29 | * Updates example to handle rotation 30 | * Update .jazzy.yaml 31 | * Link conventions to GOS-contributing repo. 32 | 33 | ## 1.0.0 34 | 35 | Initial release. 36 | 37 | ## x.x.x 38 | 39 | This is a template. When cutting a new release, rename "master" to the release number and create a 40 | new, empty "Master" section. 41 | 42 | ##### Breaking 43 | 44 | ##### Enhancements 45 | 46 | ##### Bug Fixes 47 | 48 | * This is a template description 49 | [person responsible](https://github.com/...) 50 | [#xxx](github.com/google/material-scrollview-delegate-multiplexer-ios/issues/xxx) 51 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #if !defined(__has_feature) || !__has_feature(objc_arc) 18 | #error "This file requires ARC support." 19 | #endif 20 | 21 | #import "AppDelegate.h" 22 | 23 | #import "SVDMTypicalUseViewController.h" 24 | 25 | @implementation AppDelegate 26 | 27 | - (BOOL)application:(UIApplication *)application 28 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | self.window.rootViewController = [[SVDMTypicalUseViewController alloc] init]; 31 | [self.window makeKeyAndVisible]; 32 | return YES; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## OS X System files 27 | .DS_Store 28 | 29 | ## Temporary swap files 30 | *.swp 31 | 32 | ## Obj-C/Swift specific 33 | *.hmap 34 | *.ipa 35 | 36 | # CocoaPods 37 | # 38 | # We recommend against adding the Pods directory to your .gitignore. However 39 | # you should judge for yourself, the pros and cons are mentioned at: 40 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 41 | # 42 | Pods/ 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 54 | # screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 57 | 58 | fastlane/report.xml 59 | fastlane/screenshots 60 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample/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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ## Pull requests 4 | 5 | Pull requests can be hard to review if they try to tackle too many things 6 | at once. Phabricator's "[Writing Reviewable Code](https://secure.phabricator.com/book/phabflavor/article/writing_reviewable_code/)" 7 | provides a set of guidelines that help increase the likelihood of your 8 | pull request getting merged. 9 | 10 | In short (slightly modified from the original article): 11 | 12 | - A pull request should be as small as possible, but no smaller. 13 | - The smallest a pull request can be is a single cohesive idea: don't 14 | make pull requests so small that they are meaningless on their own. 15 | - Turn large pull requests into small pull requests by dividing large 16 | problems into smaller problems and solving the small problems one at 17 | a time. 18 | - Write sensible pull request descriptions. 19 | 20 | ### Conventions 21 | 22 | This repository follows a file layout convention that ensures consistency and 23 | predictability across all of our components. The conventions are described in 24 | the [material-foundation-conventions-objc](https://github.com/material-foundation/material-foundation-conventions-objc) repository. 25 | 26 | ### Before you contribute 27 | 28 | Before we can use your code, you must sign the 29 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 30 | (CLA), which you can do online. The CLA is necessary mainly because you own the 31 | copyright to your changes, even after your contribution becomes part of our 32 | codebase, so we need your permission to use and distribute your code. We also 33 | need to be sure of various other things—for instance that you'll tell us if you 34 | know that your code infringes on other people's patents. You don't have to sign 35 | the CLA until after you've submitted your code for review and a member has 36 | approved it, but you must do it before we can put your code into our codebase. 37 | Before you start working on a larger contribution, you should get in touch with 38 | us first through the issue tracker with your idea so that we can help out and 39 | possibly guide you. Coordinating up front makes it much easier to avoid 40 | frustration later on. 41 | 42 | ### Code reviews 43 | 44 | All submissions, including submissions by project members, require review. 45 | 46 | ### The small print 47 | 48 | Contributions made by corporations are covered by a different agreement than 49 | the one above, the [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MDFScrollViewDelegateMultiplexer 2 | [![Build Status](https://travis-ci.org/material-foundation/material-scrollview-delegate-multiplexer-ios.svg?branch=develop)](https://travis-ci.org/material-foundation/material-scrollview-delegate-multiplexer-ios?branch=develop) 3 | [![Code Coverage](https://codecov.io/gh/material-foundation/material-scrollview-delegate-multiplexer-ios/branch/develop/graph/badge.svg)](http://codecov.io/github/material-foundation/material-scrollview-delegate-multiplexer-ios?branch=develop) 4 | 5 | This class acts as a proxy object for `UIScrollViewDelegate` events and forwards all received 6 | events to an ordered list of registered observers. 7 | 8 | ## Installation 9 | 10 | ### Requirements 11 | 12 | - Xcode 7.0 or higher. 13 | - iOS SDK version 9.0 or higher. 14 | 15 | ### Installation with CocoaPods 16 | 17 | To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: 18 | 19 | ~~~ bash 20 | pod 'MDFScrollViewDelegateMultiplexer' 21 | ~~~ 22 | 23 | Then, run the following command: 24 | 25 | ~~~ bash 26 | pod install 27 | ~~~ 28 | 29 | - - - 30 | 31 | ## Overview 32 | 33 | When a `UIScrollViewDelegate` method invocation is received by the multiplexer, the multiplexer 34 | forwards the invocation to each observer in order of registration. 35 | 36 | If the scroll view method signature has a return value, after all delegate method invocations, 37 | the return value will be provided by the first observing delegate that responded. If no 38 | observers implement the method, a default return value will be used. 39 | 40 | However, if a combiner is set and the receiving class conforms to the 41 | `MDFScrollViewDelegateCombining` protocol, then the receiving class can designate which result 42 | value to return via the use of the optional protocol methods. 43 | 44 | - - - 45 | 46 | 47 | ## Usage 48 | 49 | ```objectivec 50 | #import "MDFScrollViewDelegateMultiplexer.h" 51 | 52 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 53 | myScrollView.delegate = _multiplexer; 54 | [_multiplexer addObservingDelegate:myControl]; 55 | [_multiplexer addObservingDelegate:anotherControl]; 56 | ``` 57 | 58 | ## Using the `MDFScrollViewDelegateCombining` protocol 59 | 60 | First conform to the `MDFScrollViewDelegateCombining` protocol: 61 | 62 | ```objectivec 63 | @interface MyViewController () 64 | @end 65 | 66 | [_multiplexer setCombiner:self]; 67 | ``` 68 | 69 | Handle one of the `UIScrollViewDelegate` protocol methods that return a value: 70 | 71 | ```objectivec 72 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 73 | return myZoomingView; 74 | } 75 | ``` 76 | 77 | And finally return the desired observer's results from the combiner method: 78 | 79 | ```objectivec 80 | - (UIView *)scrollViewDelegateMultiplexer:(MDFScrollViewDelegateMultiplexer *)multiplexer 81 | viewForZoomingWithResults:(NSPointerArray *)results 82 | fromRespondingObservers:(NSArray *)respondingObservers { 83 | // Lets return the results from the observer which is equal to self. 84 | if (respondingObservers[0] == self) { 85 | return [results pointerAtIndex:0]; 86 | } 87 | return nil; 88 | } 89 | ``` 90 | -------------------------------------------------------------------------------- /examples/SVDMultiplexer.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 27 | 28 | 32 | 33 | 38 | 39 | 43 | 44 | 48 | 49 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample.xcodeproj/xcshareddata/xcschemes/ScrollViewDelegateMultiplexerExample.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 | -------------------------------------------------------------------------------- /examples/SVDMultiplexer.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # Scroll view delegate multiplexer playground 3 | 4 | This playground explores the use of a UIScrollViewDelegate multiplexer in order to react to UIScrollView events. 5 | 6 | Requirements: **Xcode 7.1 or higher**. 7 | 8 | We've created a framework called MDFScrollViewDelegateMultiplexer in which we've placed the relevant code. 9 | 10 | If the Playground can't find the framework then you need to initiate a build of the ScrollViewDelegateMultiplexer project. Also make sure that you're viewing the playground via Examples.xcworkspace. 11 | */ 12 | import ScrollViewDelegateMultiplexer 13 | 14 | //: We'll use the XCPlayground framework to show a live view in the Assistant editor. Simply click the Assistant editor icon to view the live view. 15 | import XCPlayground 16 | 17 | /*: 18 | ## Create a multiplexer 19 | 20 | The multiplexer object is meant to be assigned as the delegate of a UIScrollView. The delegate property won't hold onto a reference of the multiplexer, so we need to store an instance of the multiplexer elsewhere. 21 | 22 | For the purposes of this Playground we'll store the multiplexer in the global scope. In practice you'd store this multiplexer as a property on your UIViewController. 23 | */ 24 | let multiplexer = MDFScrollViewDelegateMultiplexer() 25 | 26 | /*: 27 | Let's create a hypothetical object that is interested in reacting to UIScrollViewDelegate events. When it receives a `scrollViewDidScroll:` event it will execute a provided block. 28 | */ 29 | class ScrollViewListener : NSObject { 30 | init(didScrollCallback: UIScrollView -> Void) { self.didScrollCallback = didScrollCallback } 31 | let didScrollCallback: UIScrollView -> Void 32 | } 33 | 34 | extension ScrollViewListener : UIScrollViewDelegate { 35 | func scrollViewDidScroll(scrollView: UIScrollView) { 36 | self.didScrollCallback(scrollView) 37 | } 38 | } 39 | 40 | /*: 41 | ## Register listeners 42 | 43 | We'll create two listeners objects and add both of them to our multiplexer. When the Playground executes you'll be able to verify that each block was invoked while the scroll view scrolls. 44 | */ 45 | let listener1 = ScrollViewListener { scrollView in 46 | scrollView.contentOffset.y 47 | //: Without the following return statement, Swift will treat the line above as an implicit return statement and we won't be able to see the Playground graph. 48 | return 49 | } 50 | 51 | let listener2 = ScrollViewListener { scrollView in 52 | scrollView.contentOffset.y 53 | return 54 | } 55 | 56 | //: We can now register each listener instance to the multiplexer. Our multiplexer is now ready to be assigned as the delegate of a UIScrollView. 57 | multiplexer.addObservingDelegate(listener1) 58 | multiplexer.addObservingDelegate(listener2) 59 | 60 | /*: 61 | ## The view controller 62 | 63 | We'll use the Playground live view feature to show a subclass of UIViewController we've created for this playground. 64 | */ 65 | class PlaygroundViewController : UIViewController { 66 | var scrollView: UIScrollView? 67 | } 68 | 69 | //: Assigning our view controller as the live view places will cause our view controller's view to show up in the Assistant editor. 70 | XCPlaygroundPage.currentPage.liveView = PlaygroundViewController() 71 | 72 | extension PlaygroundViewController { 73 | 74 | override func viewDidLoad() { 75 | super.viewDidLoad() 76 | self.view.backgroundColor = UIColor.whiteColor() 77 | 78 | let width = self.view.bounds.size.width 79 | 80 | scrollView = UIScrollView(frame: self.view.bounds) 81 | 82 | //: We can now assign the multiplexer as the scroll view's delegate. 83 | scrollView!.delegate = multiplexer 84 | 85 | //: The rest of this logic is straightforward UIKit view creation and manipulation. 86 | scrollView!.contentSize = CGSize(width: width, height: self.view.bounds.height * 100) 87 | self.view.addSubview(scrollView!) 88 | 89 | for _ in 0..<15 { 90 | let square = UIView(frame: CGRect( 91 | x: drand48() * Double(width - 30), 92 | y: drand48() * 1000, 93 | width: 30, 94 | height: 30) 95 | ) 96 | square.backgroundColor = UIColor.redColor().colorWithAlphaComponent(0.5) 97 | 98 | scrollView!.addSubview(square) 99 | } 100 | } 101 | 102 | override func viewDidAppear(animated: Bool) { 103 | super.viewDidAppear(animated) 104 | 105 | scrollView?.setContentOffset(CGPoint(x: 0, y: 400), animated: true) 106 | } 107 | } 108 | 109 | //: # Playground logic 110 | 111 | extension ScrollViewListener { 112 | func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { 113 | XCPlaygroundPage.currentPage.finishExecution() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/MDFScrollViewDelegateMultiplexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | @protocol MDFScrollViewDelegateCombining; 20 | 21 | /** 22 | MDFScrollViewDelegateMultiplexer acts as a proxy object for UIScrollViewDelegate events and 23 | forwards all received events to an ordered list of registered observers. 24 | 25 | When a UIScrollViewDelegate method invocation is received by the multiplexer, the multiplexer 26 | forwards the invocation to each observer in order of registration. 27 | 28 | If the scroll view method signature has a return value, after all delegate method invocations, 29 | the return value will be provided by the first observing delegate that responded. If no 30 | observers implement the method, a default return value will be used. 31 | 32 | However, if a combiner is set and the receiving class conforms to the 33 | MDFScrollViewDelegateCombining protocol, then the receiving class can designate which result 34 | value to return via the use of the optional protocol methods. 35 | 36 | Example implementation: 37 | 38 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 39 | myScrollView.delegate = _multiplexer; 40 | [_multiplexer addObservingDelegate:myControl]; 41 | [_multiplexer addObservingDelegate:anotherControl]; 42 | */ 43 | @interface MDFScrollViewDelegateMultiplexer : NSObject 44 | 45 | /** 46 | Adds an observing delegate to the end of the array of delegates. 47 | 48 | @param delegate The observing delegate to be added. 49 | */ 50 | - (void)addObservingDelegate:(nonnull id)delegate; 51 | 52 | /** 53 | Removes an observing delegate from the array of delegates. 54 | 55 | @param delegate The observing delegate to be removed. 56 | */ 57 | - (void)removeObservingDelegate:(nonnull id)delegate; 58 | 59 | /** 60 | An optional delegate through which the MDFScrollViewDelegateMultiplexer may provide a 61 | single UIScrollViewDelegate protocol return value from its array of observing delegates. 62 | */ 63 | @property(nonatomic, weak, null_resettable) id combiner; 64 | 65 | @end 66 | 67 | /** 68 | The MDFScrollViewDelegateCombining protocol defines an internal mechanism through which 69 | MDFScrollViewDelegateMultiplexer provides an array of responding observing delegates 70 | and their respective return values. 71 | 72 | Since it is possible that multiple delegates may respond to UIScrollViewDelegate methods that 73 | provide return values, this protocol allows the receiver to select the specific value to return 74 | from an array of those responding result values. 75 | */ 76 | @protocol MDFScrollViewDelegateCombining 77 | @optional 78 | 79 | /** 80 | Allows the receiver to return the preferred UIView result from observer delegates that have 81 | responded to the UIScrollViewDelegate -viewForZoomingInScrollView method. 82 | 83 | @param multiplexer The scrollView delegate multiplexer. 84 | @param results A pointer array of UIView instances returned by responding observer delegates. 85 | NSPointerArray here to allow nil results from -viewForZoomingInScrollView. 86 | @param respondingObservers An array of observing delegates that responded. 87 | 88 | @return The preferred UIView result for this method. 89 | */ 90 | - (nullable UIView *)scrollViewDelegateMultiplexer:(nonnull MDFScrollViewDelegateMultiplexer *)multiplexer 91 | viewForZoomingWithResults:(nonnull NSPointerArray *)results 92 | fromRespondingObservers:(nonnull NSArray *)respondingObservers; 93 | 94 | /** 95 | Allows the receiver to return the preferred BOOL result from observer delegates that have 96 | responded to the UIScrollViewDelegate -scrollViewShouldScrollToTop method. 97 | 98 | @param multiplexer The scrollView delegate multiplexer. 99 | @param results An array of NSNumber instances returned by responding observer delegates. 100 | @param respondingObservers An array of observing delegates that responded. 101 | 102 | @return The preferred BOOL result for this method. 103 | */ 104 | - (BOOL)scrollViewDelegateMultiplexer:(nonnull MDFScrollViewDelegateMultiplexer *)multiplexer 105 | shouldScrollToTopWithResults:(nonnull NSArray *)results 106 | fromRespondingObservers:(nonnull NSArray *)respondingObservers; 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /tests/unit/ScrollViewDelegateMultiplexerExampleTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | #import "MDFScrollViewDelegateMultiplexer.h" 20 | 21 | static NSString *const kScrollViewDidScroll = @"scrollViewDidScroll"; 22 | 23 | #pragma mark - Simple observer object 24 | 25 | /** Simple object that conforms to UIScrollViewDelegate protocol. */ 26 | @interface ScrollObservingObject : UIView 27 | @property(nonatomic) BOOL hasRecievedScrollViewDidScroll; 28 | 29 | @end 30 | 31 | @implementation ScrollObservingObject 32 | 33 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 34 | _hasRecievedScrollViewDidScroll = YES; 35 | } 36 | 37 | @end 38 | 39 | @interface ScrollViewDelegateMultiplexerExampleTests : XCTestCase 40 | @end 41 | 42 | @implementation ScrollViewDelegateMultiplexerExampleTests { 43 | UIScrollView *_scrollView; 44 | ScrollObservingObject *_observingObject; 45 | MDFScrollViewDelegateMultiplexer *_multiplexer; 46 | XCTestExpectation *_expectation; 47 | XCTestExpectation *_observerExpectation; 48 | } 49 | 50 | - (void)setUp { 51 | [super setUp]; 52 | _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 10)]; 53 | _scrollView.contentSize = CGSizeMake(200, 10); 54 | } 55 | 56 | - (void)tearDown { 57 | [super tearDown]; 58 | } 59 | 60 | #pragma mark - Tests 61 | 62 | - (void)testWithoutMultiplexer { 63 | // Given 64 | ScrollObservingObject *scrollObserver = [[ScrollObservingObject alloc] init]; 65 | _scrollView.delegate = scrollObserver; 66 | 67 | // When 68 | [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; 69 | 70 | // Then 71 | XCTAssertTrue(scrollObserver.hasRecievedScrollViewDidScroll); 72 | } 73 | 74 | - (void)testMuliplexerSingleDelegate { 75 | // Given 76 | ScrollObservingObject *scrollObserver = [[ScrollObservingObject alloc] init]; 77 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 78 | [_multiplexer addObservingDelegate:scrollObserver]; 79 | _scrollView.delegate = _multiplexer; 80 | 81 | // When 82 | [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; 83 | 84 | // Then 85 | XCTAssertTrue(scrollObserver.hasRecievedScrollViewDidScroll); 86 | } 87 | 88 | - (void)testMuliplexerMultipleDelegate { 89 | // Given 90 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 91 | _scrollView.delegate = _multiplexer; 92 | 93 | ScrollObservingObject *scrollObserver = [[ScrollObservingObject alloc] init]; 94 | [_multiplexer addObservingDelegate:scrollObserver]; 95 | ScrollObservingObject *secondScrollObserver = [[ScrollObservingObject alloc] init]; 96 | [_multiplexer addObservingDelegate:secondScrollObserver]; 97 | 98 | // When 99 | [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; 100 | 101 | // Then 102 | XCTAssertTrue(scrollObserver.hasRecievedScrollViewDidScroll); 103 | XCTAssertTrue(secondScrollObserver.hasRecievedScrollViewDidScroll); 104 | } 105 | 106 | - (void)testRemoveDelegates{ 107 | // Given 108 | ScrollObservingObject *scrollObserver = [[ScrollObservingObject alloc] init]; 109 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 110 | [_multiplexer addObservingDelegate:scrollObserver]; 111 | _scrollView.delegate = _multiplexer; 112 | 113 | // When 114 | [_multiplexer removeObservingDelegate:scrollObserver]; 115 | [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; 116 | 117 | // Then 118 | XCTAssertFalse(scrollObserver.hasRecievedScrollViewDidScroll); 119 | } 120 | 121 | - (void)testRemoveMultipleDelegates{ 122 | // Given 123 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 124 | _scrollView.delegate = _multiplexer; 125 | 126 | ScrollObservingObject *scrollObserver = [[ScrollObservingObject alloc] init]; 127 | [_multiplexer addObservingDelegate:scrollObserver]; 128 | ScrollObservingObject *secondScrollObserver = [[ScrollObservingObject alloc] init]; 129 | [_multiplexer addObservingDelegate:secondScrollObserver]; 130 | 131 | // When 132 | [_multiplexer removeObservingDelegate:secondScrollObserver]; 133 | [_multiplexer removeObservingDelegate:scrollObserver]; 134 | [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; 135 | 136 | // Then 137 | XCTAssertFalse(scrollObserver.hasRecievedScrollViewDidScroll); 138 | XCTAssertFalse(secondScrollObserver.hasRecievedScrollViewDidScroll); 139 | } 140 | 141 | - (void)testRemoveMultipleDelegatesOfTheSameObserver{ 142 | // Given 143 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 144 | _scrollView.delegate = _multiplexer; 145 | 146 | ScrollObservingObject *scrollObserver = [[ScrollObservingObject alloc] init]; 147 | [_multiplexer addObservingDelegate:scrollObserver]; 148 | [_multiplexer addObservingDelegate:scrollObserver]; 149 | 150 | // When 151 | [_multiplexer removeObservingDelegate:scrollObserver]; 152 | [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; 153 | 154 | // Then 155 | XCTAssertFalse(scrollObserver.hasRecievedScrollViewDidScroll); 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /examples/SVDMTypicalUseViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #if !defined(__has_feature) || !__has_feature(objc_arc) 18 | #error "This file requires ARC support." 19 | #endif 20 | 21 | #import "SVDMTypicalUseViewController.h" 22 | 23 | #import "MDFScrollViewDelegateMultiplexer.h" 24 | #import "ObservingPageControl.h" 25 | 26 | #define RGBCOLOR(r, g, b) [UIColor colorWithRed:(r) / 255.0f green:(g) / 255.0f blue:(b) / 255.0f alpha:1] 27 | #define HEXCOLOR(hex) RGBCOLOR((((hex) >> 16) & 0xFF), (((hex) >> 8) & 0xFF), ((hex)&0xFF)) 28 | 29 | @interface SVDMTypicalUseViewController () 30 | @end 31 | 32 | @implementation SVDMTypicalUseViewController { 33 | UIScrollView *_scrollView; 34 | UIPageControl *_pageControl; 35 | NSArray *_pageColors; 36 | MDFScrollViewDelegateMultiplexer *_multiplexer; 37 | } 38 | 39 | + (NSArray *)catalogBreadcrumbs { 40 | return @[ @"ScrollViewDelegate Multiplexer", @"Typical use" ]; 41 | } 42 | 43 | - (void)viewDidLoad { 44 | [super viewDidLoad]; 45 | 46 | CGFloat boundsWidth = CGRectGetWidth(self.view.bounds); 47 | CGFloat boundsHeight = CGRectGetHeight(self.view.bounds); 48 | 49 | _pageColors = @[ HEXCOLOR(0x81D4FA), HEXCOLOR(0x80CBC4), HEXCOLOR(0xFFCC80) ]; 50 | 51 | // Scroll view configuration 52 | 53 | _scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 54 | _scrollView.pagingEnabled = YES; 55 | _scrollView.contentSize = CGSizeMake(boundsWidth * _pageColors.count, boundsHeight); 56 | _scrollView.minimumZoomScale = 0.5; 57 | _scrollView.maximumZoomScale = 1.5; 58 | 59 | // Add pages to scrollView. 60 | for (NSInteger i = 0; i < _pageColors.count; i++) { 61 | CGRect pageFrame = CGRectOffset(self.view.bounds, i * boundsWidth, 0); 62 | UIView *page = [[UIView alloc] initWithFrame:pageFrame]; 63 | page.backgroundColor = _pageColors[i]; 64 | [_scrollView addSubview:page]; 65 | 66 | UILabel *pageTitle = [[UILabel alloc] initWithFrame:page.bounds]; 67 | pageTitle.text = [NSString stringWithFormat:@"Page %zd", i + 1]; 68 | pageTitle.font = [UIFont systemFontOfSize:50]; 69 | pageTitle.textColor = [UIColor colorWithWhite:0 alpha:0.8]; 70 | pageTitle.textAlignment = NSTextAlignmentCenter; 71 | pageTitle.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 72 | [page addSubview:pageTitle]; 73 | } 74 | 75 | // Page control configuration 76 | 77 | ObservingPageControl *pageControl = [[ObservingPageControl alloc] init]; 78 | pageControl.numberOfPages = _pageColors.count; 79 | 80 | pageControl.pageIndicatorTintColor = [UIColor colorWithWhite:0 alpha:0.2]; 81 | pageControl.currentPageIndicatorTintColor = [UIColor colorWithWhite:0 alpha:0.8]; 82 | 83 | CGSize pageControlSize = [pageControl sizeThatFits:self.view.bounds.size]; 84 | // We want the page control to span the bottom of the screen. 85 | pageControlSize.width = self.view.bounds.size.width; 86 | pageControl.frame = CGRectMake(0, 87 | self.view.bounds.size.height - pageControlSize.height, 88 | self.view.bounds.size.width, 89 | pageControlSize.height); 90 | [pageControl addTarget:self 91 | action:@selector(didChangePage:) 92 | forControlEvents:UIControlEventValueChanged]; 93 | pageControl.defersCurrentPageDisplay = YES; 94 | pageControl.autoresizingMask = UIViewAutoresizingFlexibleTopMargin 95 | | UIViewAutoresizingFlexibleLeftMargin 96 | | UIViewAutoresizingFlexibleRightMargin; 97 | _pageControl = pageControl; 98 | 99 | // Add subviews 100 | 101 | [self.view addSubview:_scrollView]; 102 | [self.view addSubview:pageControl]; 103 | 104 | // Create scrollView delegate multiplexer and register observers 105 | 106 | _multiplexer = [[MDFScrollViewDelegateMultiplexer alloc] init]; 107 | _scrollView.delegate = _multiplexer; 108 | [_multiplexer addObservingDelegate:self]; 109 | [_multiplexer addObservingDelegate:pageControl]; 110 | [_multiplexer setCombiner:self]; 111 | } 112 | 113 | #pragma mark - MDFScrollViewDelegateCombining 114 | 115 | - (UIView *)scrollViewDelegateMultiplexer:(MDFScrollViewDelegateMultiplexer *)multiplexer 116 | viewForZoomingWithResults:(NSPointerArray *)results 117 | fromRespondingObservers:(NSArray *)respondingObservers { 118 | // Lets return the results from the observer which is equal to self. 119 | if (respondingObservers[0] == self) { 120 | return [results pointerAtIndex:0]; 121 | } 122 | return nil; 123 | } 124 | 125 | #pragma mark - UIScrollViewDelegate 126 | 127 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 128 | NSLog(@"%@", NSStringFromSelector(_cmd)); 129 | 130 | [_pageControl updateCurrentPageDisplay]; 131 | } 132 | 133 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 134 | // Since there are multiple observers, we will allow the combiner to return this page 135 | // as the zooming view. 136 | return scrollView.subviews[_pageControl.currentPage]; 137 | } 138 | 139 | #pragma mark - User events 140 | 141 | - (void)didChangePage:(UIPageControl *)sender { 142 | CGPoint offset = _scrollView.contentOffset; 143 | offset.x = sender.currentPage * _scrollView.bounds.size.width; 144 | [_scrollView setContentOffset:offset animated:YES]; 145 | } 146 | 147 | #pragma mark - Rotation 148 | 149 | - (void)viewWillTransitionToSize:(CGSize)size 150 | withTransitionCoordinator:(id)coordinator { 151 | CGRect newBounds = CGRectMake(0, 0, size.width, size.height); 152 | // Adjust scrollView. 153 | _scrollView.frame = newBounds; 154 | _scrollView.contentSize = CGSizeMake(size.width * _pageColors.count, size.height); 155 | 156 | // Reset current page offset. 157 | CGPoint offset = _scrollView.contentOffset; 158 | offset.x = _pageControl.currentPage * size.width; 159 | [_scrollView setContentOffset:offset animated:YES]; 160 | 161 | // Adjust pages. 162 | for (NSInteger i = 0; i < _pageColors.count; i++) { 163 | _scrollView.subviews[i].frame = CGRectOffset(newBounds, i * size.width, 0); 164 | } 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /src/MDFScrollViewDelegateMultiplexer.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-present Google Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #if !defined(__has_feature) || !__has_feature(objc_arc) 18 | #error "This file requires ARC support." 19 | #endif 20 | 21 | #import "MDFScrollViewDelegateMultiplexer.h" 22 | 23 | #import 24 | 25 | @implementation MDFScrollViewDelegateMultiplexer { 26 | NSPointerArray *_observingDelegates; 27 | } 28 | 29 | - (instancetype)init { 30 | self = [super init]; 31 | if (self) { 32 | NSPointerFunctionsOptions options = 33 | (NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality); 34 | _observingDelegates = [NSPointerArray pointerArrayWithOptions:options]; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)addObservingDelegate:(id)delegate { 40 | [_observingDelegates addPointer:(__bridge void *)(delegate)]; 41 | } 42 | 43 | - (void)removeObservingDelegate:(id)delegate { 44 | for (NSInteger i = _observingDelegates.count -1; i >= 0; i--) { 45 | if ([_observingDelegates pointerAtIndex:i] == (__bridge void *)(delegate)) { 46 | [_observingDelegates removePointerAtIndex:i]; 47 | } 48 | } 49 | } 50 | 51 | - (void)setCombiner:(id)combiner { 52 | _combiner = combiner; 53 | } 54 | 55 | #pragma mark - NSObject 56 | 57 | - (BOOL)shouldForwardSelector:(SEL)selector { 58 | // Check optional methods. 59 | struct objc_method_description description = 60 | protocol_getMethodDescription(@protocol(UIScrollViewDelegate), selector, NO, YES); 61 | return (description.name != NULL && description.types != NULL); 62 | } 63 | 64 | - (BOOL)respondsToSelector:(SEL)aSelector { 65 | if ([super respondsToSelector:aSelector]) { 66 | return YES; 67 | } else if ([self shouldForwardSelector:aSelector]) { 68 | for (id delegate in _observingDelegates) { 69 | if ([delegate respondsToSelector:aSelector]) { 70 | return YES; 71 | } 72 | } 73 | } 74 | return NO; 75 | } 76 | 77 | #pragma mark - UIScrollViewDelegate Forwarding 78 | 79 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 80 | for (id delegate in _observingDelegates) { 81 | if ([delegate respondsToSelector:_cmd]) { 82 | [delegate scrollViewDidScroll:scrollView]; 83 | } 84 | } 85 | } 86 | 87 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView { 88 | for (id delegate in _observingDelegates) { 89 | if ([delegate respondsToSelector:_cmd]) { 90 | [delegate scrollViewDidZoom:scrollView]; 91 | } 92 | } 93 | } 94 | 95 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 96 | for (id delegate in _observingDelegates) { 97 | if ([delegate respondsToSelector:_cmd]) { 98 | [delegate scrollViewWillBeginDragging:scrollView]; 99 | } 100 | } 101 | } 102 | 103 | - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 104 | withVelocity:(CGPoint)velocity 105 | targetContentOffset:(inout CGPoint *)targetContentOffset { 106 | for (id delegate in _observingDelegates) { 107 | if ([delegate respondsToSelector:_cmd]) { 108 | [delegate scrollViewWillEndDragging:scrollView 109 | withVelocity:velocity 110 | targetContentOffset:targetContentOffset]; 111 | } 112 | } 113 | } 114 | 115 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 116 | for (id delegate in _observingDelegates) { 117 | if ([delegate respondsToSelector:_cmd]) { 118 | [delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; 119 | } 120 | } 121 | } 122 | 123 | - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView { 124 | for (id delegate in _observingDelegates) { 125 | if ([delegate respondsToSelector:_cmd]) { 126 | [delegate scrollViewWillBeginDecelerating:scrollView]; 127 | } 128 | } 129 | } 130 | 131 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 132 | for (id delegate in _observingDelegates) { 133 | if ([delegate respondsToSelector:_cmd]) { 134 | [delegate scrollViewDidEndDecelerating:scrollView]; 135 | } 136 | } 137 | } 138 | 139 | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { 140 | for (id delegate in _observingDelegates) { 141 | if ([delegate respondsToSelector:_cmd]) { 142 | [delegate scrollViewDidEndScrollingAnimation:scrollView]; 143 | } 144 | } 145 | } 146 | 147 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 148 | // NSPointerArray here to allow nil results from -viewForZoomingInScrollView. 149 | NSPointerArray *results = 150 | [NSPointerArray pointerArrayWithOptions:(NSPointerFunctionsStrongMemory | 151 | NSPointerFunctionsObjectPointerPersonality)]; 152 | NSMutableArray *respondingObservers = [NSMutableArray array]; 153 | 154 | // Execute this method for every responding observer. 155 | for (id delegate in _observingDelegates) { 156 | if ([delegate respondsToSelector:_cmd]) { 157 | UIView *result = [delegate viewForZoomingInScrollView:scrollView]; 158 | [results addPointer:(__bridge void *)(result)]; 159 | [respondingObservers addObject:delegate]; 160 | } 161 | } 162 | 163 | if ([_combiner respondsToSelector:@selector(scrollViewDelegateMultiplexer: 164 | viewForZoomingWithResults: 165 | fromRespondingObservers:)]) { 166 | return [_combiner scrollViewDelegateMultiplexer:(id)self 167 | viewForZoomingWithResults:results 168 | fromRespondingObservers:respondingObservers]; 169 | } else if (results.count > 0) { 170 | #if DEBUG 171 | NSHashTable *hash = [NSHashTable weakObjectsHashTable]; 172 | for (UIView *view in results) { 173 | [hash addObject:view ? view : [NSNull null]]; 174 | } 175 | NSAssert(hash.count == 1, 176 | @"-viewForZoomingInScrollView returns different results from multiple observers." 177 | " Use the combiner protocol MDFScrollViewDelegateCombining to select the appropriate" 178 | " observer return value for this method."); 179 | #endif 180 | 181 | return [results pointerAtIndex:0]; 182 | } 183 | 184 | return nil; 185 | } 186 | 187 | - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView 188 | withView:(UIView *)view { 189 | for (id delegate in _observingDelegates) { 190 | if ([delegate respondsToSelector:_cmd]) { 191 | [delegate scrollViewWillBeginZooming:scrollView withView:view]; 192 | } 193 | } 194 | } 195 | 196 | - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView 197 | withView:(UIView *)view 198 | atScale:(CGFloat)scale { 199 | for (id delegate in _observingDelegates) { 200 | if ([delegate respondsToSelector:_cmd]) { 201 | [delegate scrollViewDidEndZooming:scrollView withView:view atScale:scale]; 202 | } 203 | } 204 | } 205 | 206 | - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { 207 | NSMutableArray *results = [NSMutableArray array]; 208 | NSMutableArray *respondingObservers = [NSMutableArray array]; 209 | 210 | // Execute this method for every responding observer. 211 | for (id delegate in _observingDelegates) { 212 | if ([delegate respondsToSelector:_cmd]) { 213 | [results addObject:@([delegate scrollViewShouldScrollToTop:scrollView])]; 214 | [respondingObservers addObject:delegate]; 215 | } 216 | } 217 | 218 | if ([_combiner respondsToSelector:@selector(scrollViewDelegateMultiplexer: 219 | shouldScrollToTopWithResults: 220 | fromRespondingObservers:)]) { 221 | return [_combiner scrollViewDelegateMultiplexer:(id)self 222 | shouldScrollToTopWithResults:results 223 | fromRespondingObservers:respondingObservers]; 224 | } else if (results.count > 0) { 225 | #if DEBUG 226 | NSSet *set = [NSSet setWithArray:results]; 227 | NSAssert(set.count == 1, 228 | @"-scrollViewShouldScrollToTop returns different results from multiple observers." 229 | " Use the combiner protocol MDFScrollViewDelegateCombining to select the appropriate" 230 | " observer return value for this method."); 231 | #endif 232 | 233 | return [results[0] boolValue]; 234 | } 235 | 236 | return YES; 237 | } 238 | 239 | - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { 240 | for (id delegate in _observingDelegates) { 241 | if ([delegate respondsToSelector:_cmd]) { 242 | [delegate scrollViewDidScrollToTop:scrollView]; 243 | } 244 | } 245 | } 246 | 247 | @end 248 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /examples/app/ScrollViewDelegateMultiplexerExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5D64A5001CB44F1F00EC4788 /* ScrollViewDelegateMultiplexerExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D64A4FD1CB44F1F00EC4788 /* ScrollViewDelegateMultiplexerExampleTests.m */; }; 11 | 5D64A5011CB44F1F00EC4788 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5D64A4FF1CB44F1F00EC4788 /* Info.plist */; }; 12 | 666CA7581CB41B6F001B1884 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA7571CB41B6F001B1884 /* main.m */; }; 13 | 666CA75B1CB41B6F001B1884 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA75A1CB41B6F001B1884 /* AppDelegate.m */; }; 14 | 666CA7631CB41B6F001B1884 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 666CA7621CB41B6F001B1884 /* Assets.xcassets */; }; 15 | 666CA7661CB41B6F001B1884 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 666CA7641CB41B6F001B1884 /* LaunchScreen.storyboard */; }; 16 | 666CA7751CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 666CA7741CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 666CA7791CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 666CA7721CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework */; }; 18 | 666CA77A1CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 666CA7721CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | 666CA7821CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 666CA7801CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | 666CA7831CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA7811CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.m */; }; 21 | 666CA7881CB41C0C001B1884 /* ObservingPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA7851CB41C0C001B1884 /* ObservingPageControl.m */; }; 22 | 666CA7891CB41C0C001B1884 /* SVDMTypicalUseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA7871CB41C0C001B1884 /* SVDMTypicalUseViewController.m */; }; 23 | 666CA79A1CB41C7E001B1884 /* ScrollViewDelegateMultiplexer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 666CA7721CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 666CA7771CB41BBB001B1884 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 666CA74B1CB41B6F001B1884 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 666CA7711CB41BBB001B1884; 32 | remoteInfo = ScrollViewDelegateMultiplexer; 33 | }; 34 | 666CA79B1CB41C83001B1884 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 666CA74B1CB41B6F001B1884 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 666CA7711CB41BBB001B1884; 39 | remoteInfo = ScrollViewDelegateMultiplexer; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXCopyFilesBuildPhase section */ 44 | 666CA77E1CB41BBC001B1884 /* Embed Frameworks */ = { 45 | isa = PBXCopyFilesBuildPhase; 46 | buildActionMask = 2147483647; 47 | dstPath = ""; 48 | dstSubfolderSpec = 10; 49 | files = ( 50 | 666CA77A1CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework in Embed Frameworks */, 51 | ); 52 | name = "Embed Frameworks"; 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXCopyFilesBuildPhase section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 5D64A4FD1CB44F1F00EC4788 /* ScrollViewDelegateMultiplexerExampleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ScrollViewDelegateMultiplexerExampleTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 59 | 5D64A4FF1CB44F1F00EC4788 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 666CA7531CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScrollViewDelegateMultiplexerExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 666CA7571CB41B6F001B1884 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 62 | 666CA7591CB41B6F001B1884 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 63 | 666CA75A1CB41B6F001B1884 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 64 | 666CA7621CB41B6F001B1884 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 65 | 666CA7651CB41B6F001B1884 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 66 | 666CA7671CB41B6F001B1884 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 666CA7721CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ScrollViewDelegateMultiplexer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 666CA7741CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollViewDelegateMultiplexer.h; sourceTree = ""; }; 69 | 666CA7761CB41BBB001B1884 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 666CA7801CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = MDFScrollViewDelegateMultiplexer.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 71 | 666CA7811CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDFScrollViewDelegateMultiplexer.m; sourceTree = ""; }; 72 | 666CA7841CB41C0C001B1884 /* ObservingPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObservingPageControl.h; path = ../../ObservingPageControl.h; sourceTree = ""; }; 73 | 666CA7851CB41C0C001B1884 /* ObservingPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ObservingPageControl.m; path = ../../ObservingPageControl.m; sourceTree = ""; }; 74 | 666CA7861CB41C0C001B1884 /* SVDMTypicalUseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SVDMTypicalUseViewController.h; path = ../../SVDMTypicalUseViewController.h; sourceTree = ""; }; 75 | 666CA7871CB41C0C001B1884 /* SVDMTypicalUseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SVDMTypicalUseViewController.m; path = ../../SVDMTypicalUseViewController.m; sourceTree = ""; }; 76 | 666CA78E1CB41C53001B1884 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFrameworksBuildPhase section */ 80 | 666CA7501CB41B6F001B1884 /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | 666CA7791CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | 666CA76E1CB41BBB001B1884 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | 666CA78B1CB41C53001B1884 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | 666CA79A1CB41C7E001B1884 /* ScrollViewDelegateMultiplexer.framework in Frameworks */, 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | /* End PBXFrameworksBuildPhase section */ 104 | 105 | /* Begin PBXGroup section */ 106 | 5D64A4FB1CB44F1F00EC4788 /* tests */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 5D64A4FC1CB44F1F00EC4788 /* unit */, 110 | ); 111 | name = tests; 112 | path = ../../tests; 113 | sourceTree = ""; 114 | }; 115 | 5D64A4FC1CB44F1F00EC4788 /* unit */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 5D64A4FD1CB44F1F00EC4788 /* ScrollViewDelegateMultiplexerExampleTests.m */, 119 | 5D64A4FE1CB44F1F00EC4788 /* resources */, 120 | ); 121 | path = unit; 122 | sourceTree = ""; 123 | }; 124 | 5D64A4FE1CB44F1F00EC4788 /* resources */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 5D64A4FF1CB44F1F00EC4788 /* Info.plist */, 128 | ); 129 | path = resources; 130 | sourceTree = ""; 131 | }; 132 | 666CA74A1CB41B6F001B1884 = { 133 | isa = PBXGroup; 134 | children = ( 135 | 666CA7551CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample */, 136 | 666CA7731CB41BBB001B1884 /* ScrollViewDelegateMultiplexer */, 137 | 5D64A4FB1CB44F1F00EC4788 /* tests */, 138 | 666CA7541CB41B6F001B1884 /* Products */, 139 | ); 140 | sourceTree = ""; 141 | }; 142 | 666CA7541CB41B6F001B1884 /* Products */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 666CA7531CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample.app */, 146 | 666CA7721CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework */, 147 | 666CA78E1CB41C53001B1884 /* UnitTests.xctest */, 148 | ); 149 | name = Products; 150 | sourceTree = ""; 151 | }; 152 | 666CA7551CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 666CA7591CB41B6F001B1884 /* AppDelegate.h */, 156 | 666CA75A1CB41B6F001B1884 /* AppDelegate.m */, 157 | 666CA7841CB41C0C001B1884 /* ObservingPageControl.h */, 158 | 666CA7851CB41C0C001B1884 /* ObservingPageControl.m */, 159 | 666CA7861CB41C0C001B1884 /* SVDMTypicalUseViewController.h */, 160 | 666CA7871CB41C0C001B1884 /* SVDMTypicalUseViewController.m */, 161 | 666CA7621CB41B6F001B1884 /* Assets.xcassets */, 162 | 666CA7641CB41B6F001B1884 /* LaunchScreen.storyboard */, 163 | 666CA7671CB41B6F001B1884 /* Info.plist */, 164 | 666CA7561CB41B6F001B1884 /* Supporting Files */, 165 | ); 166 | path = ScrollViewDelegateMultiplexerExample; 167 | sourceTree = ""; 168 | }; 169 | 666CA7561CB41B6F001B1884 /* Supporting Files */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 666CA7571CB41B6F001B1884 /* main.m */, 173 | ); 174 | name = "Supporting Files"; 175 | sourceTree = ""; 176 | }; 177 | 666CA7731CB41BBB001B1884 /* ScrollViewDelegateMultiplexer */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 666CA77F1CB41BD5001B1884 /* src */, 181 | 666CA7741CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.h */, 182 | 666CA7761CB41BBB001B1884 /* Info.plist */, 183 | ); 184 | path = ScrollViewDelegateMultiplexer; 185 | sourceTree = ""; 186 | }; 187 | 666CA77F1CB41BD5001B1884 /* src */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 666CA7801CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.h */, 191 | 666CA7811CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.m */, 192 | ); 193 | name = src; 194 | path = ../../../src; 195 | sourceTree = ""; 196 | }; 197 | /* End PBXGroup section */ 198 | 199 | /* Begin PBXHeadersBuildPhase section */ 200 | 666CA76F1CB41BBB001B1884 /* Headers */ = { 201 | isa = PBXHeadersBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 666CA7751CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.h in Headers */, 205 | 666CA7821CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.h in Headers */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXHeadersBuildPhase section */ 210 | 211 | /* Begin PBXNativeTarget section */ 212 | 666CA7521CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = 666CA76A1CB41B6F001B1884 /* Build configuration list for PBXNativeTarget "ScrollViewDelegateMultiplexerExample" */; 215 | buildPhases = ( 216 | 666CA74F1CB41B6F001B1884 /* Sources */, 217 | 666CA7501CB41B6F001B1884 /* Frameworks */, 218 | 666CA7511CB41B6F001B1884 /* Resources */, 219 | 666CA77E1CB41BBC001B1884 /* Embed Frameworks */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | 666CA7781CB41BBB001B1884 /* PBXTargetDependency */, 225 | ); 226 | name = ScrollViewDelegateMultiplexerExample; 227 | productName = ScrollViewDelegateMultiplexerExample; 228 | productReference = 666CA7531CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample.app */; 229 | productType = "com.apple.product-type.application"; 230 | }; 231 | 666CA7711CB41BBB001B1884 /* ScrollViewDelegateMultiplexer */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = 666CA77D1CB41BBC001B1884 /* Build configuration list for PBXNativeTarget "ScrollViewDelegateMultiplexer" */; 234 | buildPhases = ( 235 | 666CA76D1CB41BBB001B1884 /* Sources */, 236 | 666CA76E1CB41BBB001B1884 /* Frameworks */, 237 | 666CA76F1CB41BBB001B1884 /* Headers */, 238 | 666CA7701CB41BBB001B1884 /* Resources */, 239 | ); 240 | buildRules = ( 241 | ); 242 | dependencies = ( 243 | ); 244 | name = ScrollViewDelegateMultiplexer; 245 | productName = ScrollViewDelegateMultiplexer; 246 | productReference = 666CA7721CB41BBB001B1884 /* ScrollViewDelegateMultiplexer.framework */; 247 | productType = "com.apple.product-type.framework"; 248 | }; 249 | 666CA78D1CB41C53001B1884 /* UnitTests */ = { 250 | isa = PBXNativeTarget; 251 | buildConfigurationList = 666CA7951CB41C53001B1884 /* Build configuration list for PBXNativeTarget "UnitTests" */; 252 | buildPhases = ( 253 | 666CA78A1CB41C53001B1884 /* Sources */, 254 | 666CA78B1CB41C53001B1884 /* Frameworks */, 255 | 666CA78C1CB41C53001B1884 /* Resources */, 256 | ); 257 | buildRules = ( 258 | ); 259 | dependencies = ( 260 | 666CA79C1CB41C83001B1884 /* PBXTargetDependency */, 261 | ); 262 | name = UnitTests; 263 | productName = UnitTests; 264 | productReference = 666CA78E1CB41C53001B1884 /* UnitTests.xctest */; 265 | productType = "com.apple.product-type.bundle.unit-test"; 266 | }; 267 | /* End PBXNativeTarget section */ 268 | 269 | /* Begin PBXProject section */ 270 | 666CA74B1CB41B6F001B1884 /* Project object */ = { 271 | isa = PBXProject; 272 | attributes = { 273 | LastUpgradeCheck = 0720; 274 | ORGANIZATIONNAME = Google; 275 | TargetAttributes = { 276 | 666CA7521CB41B6F001B1884 = { 277 | CreatedOnToolsVersion = 7.2.1; 278 | }; 279 | 666CA7711CB41BBB001B1884 = { 280 | CreatedOnToolsVersion = 7.2.1; 281 | }; 282 | 666CA78D1CB41C53001B1884 = { 283 | CreatedOnToolsVersion = 7.2.1; 284 | TestTargetID = 666CA7521CB41B6F001B1884; 285 | }; 286 | }; 287 | }; 288 | buildConfigurationList = 666CA74E1CB41B6F001B1884 /* Build configuration list for PBXProject "ScrollViewDelegateMultiplexerExample" */; 289 | compatibilityVersion = "Xcode 3.2"; 290 | developmentRegion = English; 291 | hasScannedForEncodings = 0; 292 | knownRegions = ( 293 | en, 294 | Base, 295 | ); 296 | mainGroup = 666CA74A1CB41B6F001B1884; 297 | productRefGroup = 666CA7541CB41B6F001B1884 /* Products */; 298 | projectDirPath = ""; 299 | projectRoot = ""; 300 | targets = ( 301 | 666CA7521CB41B6F001B1884 /* ScrollViewDelegateMultiplexerExample */, 302 | 666CA7711CB41BBB001B1884 /* ScrollViewDelegateMultiplexer */, 303 | 666CA78D1CB41C53001B1884 /* UnitTests */, 304 | ); 305 | }; 306 | /* End PBXProject section */ 307 | 308 | /* Begin PBXResourcesBuildPhase section */ 309 | 666CA7511CB41B6F001B1884 /* Resources */ = { 310 | isa = PBXResourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | 666CA7661CB41B6F001B1884 /* LaunchScreen.storyboard in Resources */, 314 | 666CA7631CB41B6F001B1884 /* Assets.xcassets in Resources */, 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | 666CA7701CB41BBB001B1884 /* Resources */ = { 319 | isa = PBXResourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | 666CA78C1CB41C53001B1884 /* Resources */ = { 326 | isa = PBXResourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | 5D64A5011CB44F1F00EC4788 /* Info.plist in Resources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | /* End PBXResourcesBuildPhase section */ 334 | 335 | /* Begin PBXSourcesBuildPhase section */ 336 | 666CA74F1CB41B6F001B1884 /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 666CA7881CB41C0C001B1884 /* ObservingPageControl.m in Sources */, 341 | 666CA7891CB41C0C001B1884 /* SVDMTypicalUseViewController.m in Sources */, 342 | 666CA75B1CB41B6F001B1884 /* AppDelegate.m in Sources */, 343 | 666CA7581CB41B6F001B1884 /* main.m in Sources */, 344 | ); 345 | runOnlyForDeploymentPostprocessing = 0; 346 | }; 347 | 666CA76D1CB41BBB001B1884 /* Sources */ = { 348 | isa = PBXSourcesBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | 666CA7831CB41BDF001B1884 /* MDFScrollViewDelegateMultiplexer.m in Sources */, 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | 666CA78A1CB41C53001B1884 /* Sources */ = { 356 | isa = PBXSourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | 5D64A5001CB44F1F00EC4788 /* ScrollViewDelegateMultiplexerExampleTests.m in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | /* End PBXSourcesBuildPhase section */ 364 | 365 | /* Begin PBXTargetDependency section */ 366 | 666CA7781CB41BBB001B1884 /* PBXTargetDependency */ = { 367 | isa = PBXTargetDependency; 368 | target = 666CA7711CB41BBB001B1884 /* ScrollViewDelegateMultiplexer */; 369 | targetProxy = 666CA7771CB41BBB001B1884 /* PBXContainerItemProxy */; 370 | }; 371 | 666CA79C1CB41C83001B1884 /* PBXTargetDependency */ = { 372 | isa = PBXTargetDependency; 373 | target = 666CA7711CB41BBB001B1884 /* ScrollViewDelegateMultiplexer */; 374 | targetProxy = 666CA79B1CB41C83001B1884 /* PBXContainerItemProxy */; 375 | }; 376 | /* End PBXTargetDependency section */ 377 | 378 | /* Begin PBXVariantGroup section */ 379 | 666CA7641CB41B6F001B1884 /* LaunchScreen.storyboard */ = { 380 | isa = PBXVariantGroup; 381 | children = ( 382 | 666CA7651CB41B6F001B1884 /* Base */, 383 | ); 384 | name = LaunchScreen.storyboard; 385 | sourceTree = ""; 386 | }; 387 | /* End PBXVariantGroup section */ 388 | 389 | /* Begin XCBuildConfiguration section */ 390 | 666CA7681CB41B6F001B1884 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 395 | CLANG_CXX_LIBRARY = "libc++"; 396 | CLANG_ENABLE_MODULES = YES; 397 | CLANG_ENABLE_OBJC_ARC = YES; 398 | CLANG_WARN_BOOL_CONVERSION = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 401 | CLANG_WARN_EMPTY_BODY = YES; 402 | CLANG_WARN_ENUM_CONVERSION = YES; 403 | CLANG_WARN_INT_CONVERSION = YES; 404 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 405 | CLANG_WARN_UNREACHABLE_CODE = YES; 406 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 407 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 408 | COPY_PHASE_STRIP = NO; 409 | DEBUG_INFORMATION_FORMAT = dwarf; 410 | ENABLE_STRICT_OBJC_MSGSEND = YES; 411 | ENABLE_TESTABILITY = YES; 412 | GCC_C_LANGUAGE_STANDARD = gnu99; 413 | GCC_DYNAMIC_NO_PIC = NO; 414 | GCC_NO_COMMON_BLOCKS = YES; 415 | GCC_OPTIMIZATION_LEVEL = 0; 416 | GCC_PREPROCESSOR_DEFINITIONS = ( 417 | "DEBUG=1", 418 | "$(inherited)", 419 | ); 420 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 421 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 422 | GCC_WARN_UNDECLARED_SELECTOR = YES; 423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 424 | GCC_WARN_UNUSED_FUNCTION = YES; 425 | GCC_WARN_UNUSED_VARIABLE = YES; 426 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 427 | MTL_ENABLE_DEBUG_INFO = YES; 428 | ONLY_ACTIVE_ARCH = YES; 429 | SDKROOT = iphoneos; 430 | TARGETED_DEVICE_FAMILY = "1,2"; 431 | }; 432 | name = Debug; 433 | }; 434 | 666CA7691CB41B6F001B1884 /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 439 | CLANG_CXX_LIBRARY = "libc++"; 440 | CLANG_ENABLE_MODULES = YES; 441 | CLANG_ENABLE_OBJC_ARC = YES; 442 | CLANG_WARN_BOOL_CONVERSION = YES; 443 | CLANG_WARN_CONSTANT_CONVERSION = YES; 444 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 445 | CLANG_WARN_EMPTY_BODY = YES; 446 | CLANG_WARN_ENUM_CONVERSION = YES; 447 | CLANG_WARN_INT_CONVERSION = YES; 448 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 449 | CLANG_WARN_UNREACHABLE_CODE = YES; 450 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 451 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 452 | COPY_PHASE_STRIP = NO; 453 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 454 | ENABLE_NS_ASSERTIONS = NO; 455 | ENABLE_STRICT_OBJC_MSGSEND = YES; 456 | GCC_C_LANGUAGE_STANDARD = gnu99; 457 | GCC_NO_COMMON_BLOCKS = YES; 458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 460 | GCC_WARN_UNDECLARED_SELECTOR = YES; 461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 462 | GCC_WARN_UNUSED_FUNCTION = YES; 463 | GCC_WARN_UNUSED_VARIABLE = YES; 464 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 465 | MTL_ENABLE_DEBUG_INFO = NO; 466 | SDKROOT = iphoneos; 467 | TARGETED_DEVICE_FAMILY = "1,2"; 468 | VALIDATE_PRODUCT = YES; 469 | }; 470 | name = Release; 471 | }; 472 | 666CA76B1CB41B6F001B1884 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 476 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 477 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 478 | INFOPLIST_FILE = ScrollViewDelegateMultiplexerExample/Info.plist; 479 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 481 | PRODUCT_BUNDLE_IDENTIFIER = com.google.ScrollViewDelegateMultiplexerExample; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | }; 484 | name = Debug; 485 | }; 486 | 666CA76C1CB41B6F001B1884 /* Release */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 490 | CLANG_ENABLE_CODE_COVERAGE = NO; 491 | INFOPLIST_FILE = ScrollViewDelegateMultiplexerExample/Info.plist; 492 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 494 | PRODUCT_BUNDLE_IDENTIFIER = com.google.ScrollViewDelegateMultiplexerExample; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | }; 497 | name = Release; 498 | }; 499 | 666CA77B1CB41BBC001B1884 /* Debug */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | CURRENT_PROJECT_VERSION = 1; 503 | DEFINES_MODULE = YES; 504 | DYLIB_COMPATIBILITY_VERSION = 1; 505 | DYLIB_CURRENT_VERSION = 1; 506 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 507 | INFOPLIST_FILE = ScrollViewDelegateMultiplexer/Info.plist; 508 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 509 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 510 | PRODUCT_BUNDLE_IDENTIFIER = com.google.ScrollViewDelegateMultiplexer; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | SKIP_INSTALL = YES; 513 | VERSIONING_SYSTEM = "apple-generic"; 514 | VERSION_INFO_PREFIX = ""; 515 | }; 516 | name = Debug; 517 | }; 518 | 666CA77C1CB41BBC001B1884 /* Release */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | CURRENT_PROJECT_VERSION = 1; 522 | DEFINES_MODULE = YES; 523 | DYLIB_COMPATIBILITY_VERSION = 1; 524 | DYLIB_CURRENT_VERSION = 1; 525 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 526 | INFOPLIST_FILE = ScrollViewDelegateMultiplexer/Info.plist; 527 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 528 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 529 | PRODUCT_BUNDLE_IDENTIFIER = com.google.ScrollViewDelegateMultiplexer; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | SKIP_INSTALL = YES; 532 | VERSIONING_SYSTEM = "apple-generic"; 533 | VERSION_INFO_PREFIX = ""; 534 | }; 535 | name = Release; 536 | }; 537 | 666CA7961CB41C53001B1884 /* Debug */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | BUNDLE_LOADER = "$(TEST_HOST)"; 541 | INFOPLIST_FILE = ../../tests/unit/resources/Info.plist; 542 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 543 | PRODUCT_BUNDLE_IDENTIFIER = com.google.UnitTests; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScrollViewDelegateMultiplexerExample.app/ScrollViewDelegateMultiplexerExample"; 546 | }; 547 | name = Debug; 548 | }; 549 | 666CA7971CB41C53001B1884 /* Release */ = { 550 | isa = XCBuildConfiguration; 551 | buildSettings = { 552 | BUNDLE_LOADER = "$(TEST_HOST)"; 553 | INFOPLIST_FILE = ../../tests/unit/resources/Info.plist; 554 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 555 | PRODUCT_BUNDLE_IDENTIFIER = com.google.UnitTests; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScrollViewDelegateMultiplexerExample.app/ScrollViewDelegateMultiplexerExample"; 558 | }; 559 | name = Release; 560 | }; 561 | /* End XCBuildConfiguration section */ 562 | 563 | /* Begin XCConfigurationList section */ 564 | 666CA74E1CB41B6F001B1884 /* Build configuration list for PBXProject "ScrollViewDelegateMultiplexerExample" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 666CA7681CB41B6F001B1884 /* Debug */, 568 | 666CA7691CB41B6F001B1884 /* Release */, 569 | ); 570 | defaultConfigurationIsVisible = 0; 571 | defaultConfigurationName = Release; 572 | }; 573 | 666CA76A1CB41B6F001B1884 /* Build configuration list for PBXNativeTarget "ScrollViewDelegateMultiplexerExample" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | 666CA76B1CB41B6F001B1884 /* Debug */, 577 | 666CA76C1CB41B6F001B1884 /* Release */, 578 | ); 579 | defaultConfigurationIsVisible = 0; 580 | defaultConfigurationName = Release; 581 | }; 582 | 666CA77D1CB41BBC001B1884 /* Build configuration list for PBXNativeTarget "ScrollViewDelegateMultiplexer" */ = { 583 | isa = XCConfigurationList; 584 | buildConfigurations = ( 585 | 666CA77B1CB41BBC001B1884 /* Debug */, 586 | 666CA77C1CB41BBC001B1884 /* Release */, 587 | ); 588 | defaultConfigurationIsVisible = 0; 589 | defaultConfigurationName = Release; 590 | }; 591 | 666CA7951CB41C53001B1884 /* Build configuration list for PBXNativeTarget "UnitTests" */ = { 592 | isa = XCConfigurationList; 593 | buildConfigurations = ( 594 | 666CA7961CB41C53001B1884 /* Debug */, 595 | 666CA7971CB41C53001B1884 /* Release */, 596 | ); 597 | defaultConfigurationIsVisible = 0; 598 | defaultConfigurationName = Release; 599 | }; 600 | /* End XCConfigurationList section */ 601 | }; 602 | rootObject = 666CA74B1CB41B6F001B1884 /* Project object */; 603 | } 604 | --------------------------------------------------------------------------------