diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a816859 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - "**" + schedule: + - cron: '3 3 * * 2' # 3:03 AM, every Tuesday + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + macOS: + name: ${{ matrix.platform }} (Swift ${{ matrix.swift }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-12 + platform: + - iOS + - mac-catalyst + - tvOS + swift: + - 5.5 + - 5.6 + - 5.7 + steps: + - uses: actions/checkout@v2 + - name: Test Popsicle + uses: mxcl/xcodebuild@v1 + with: + platform: ${{ matrix.platform }} + swift: ~${{ matrix.swift }} + action: test + scheme: Popsicle + - if: ${{ matrix.swift >= 5.7 }} + name: Build Demo + uses: mxcl/xcodebuild@v1 + with: + platform: ${{ matrix.platform }} + swift: ~${{ matrix.swift }} + action: build + scheme: Demo diff --git a/.gitignore b/.gitignore index 91fef9b..b6a29e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,10 @@ -# OS X - .DS_Store - -# Xcode - -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate +/.build +/Packages +/.swiftpm +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Assets/1.gif b/Assets/1.gif deleted file mode 100755 index 080702e..0000000 Binary files a/Assets/1.gif and /dev/null differ diff --git a/Assets/header.png b/Assets/header.png deleted file mode 100755 index 32608fe..0000000 Binary files a/Assets/header.png and /dev/null differ diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c309d59 --- /dev/null +++ b/Demo/Demo.xcodeproj/project.pbxproj @@ -0,0 +1,388 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + D5934B1829272EF2002FFC24 /* PageScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5934B1729272EF2002FFC24 /* PageScrollView.swift */; }; + D5934B1B29284AE2002FFC24 /* EasingFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5934B1A29284AE2002FFC24 /* EasingFunctions.swift */; }; + D5B041942926AA680019620D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D5B041932926AA680019620D /* Assets.xcassets */; }; + D5B041A02926AAFF0019620D /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B0419D2926AAFF0019620D /* AppView.swift */; }; + D5B041A12926AAFF0019620D /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B0419E2926AAFF0019620D /* AppViewController.swift */; }; + D5B041A22926AAFF0019620D /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B0419F2926AAFF0019620D /* App.swift */; }; + D5B041A52926AB3A0019620D /* Popsicle in Frameworks */ = {isa = PBXBuildFile; productRef = D5B041A42926AB3A0019620D /* Popsicle */; }; + D5B041A72926AE8A0019620D /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B041A62926AE8A0019620D /* Helpers.swift */; }; + D5B041AB2926D5D40019620D /* DRPageScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = D5B041AA2926D5D40019620D /* DRPageScrollView.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + D5934B1729272EF2002FFC24 /* PageScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageScrollView.swift; sourceTree = ""; }; + D5934B19292845D7002FFC24 /* Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Demo.entitlements; sourceTree = ""; }; + D5934B1A29284AE2002FFC24 /* EasingFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EasingFunctions.swift; sourceTree = ""; }; + D5B0418C2926AA670019620D /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D5B041932926AA680019620D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + D5B0419D2926AAFF0019620D /* AppView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; }; + D5B0419E2926AAFF0019620D /* AppViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = ""; }; + D5B0419F2926AAFF0019620D /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; + D5B041A62926AE8A0019620D /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; + D5B041A82926D5D30019620D /* Demo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Demo-Bridging-Header.h"; sourceTree = ""; }; + D5B041A92926D5D40019620D /* DRPageScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DRPageScrollView.h; sourceTree = ""; }; + D5B041AA2926D5D40019620D /* DRPageScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DRPageScrollView.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D5B041892926AA670019620D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D5B041A52926AB3A0019620D /* Popsicle in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D5B041832926AA670019620D = { + isa = PBXGroup; + children = ( + D5B0418E2926AA670019620D /* Demo */, + D5B0418D2926AA670019620D /* Products */, + D5B041A32926AB3A0019620D /* Frameworks */, + ); + sourceTree = ""; + }; + D5B0418D2926AA670019620D /* Products */ = { + isa = PBXGroup; + children = ( + D5B0418C2926AA670019620D /* Demo.app */, + ); + name = Products; + sourceTree = ""; + }; + D5B0418E2926AA670019620D /* Demo */ = { + isa = PBXGroup; + children = ( + D5934B19292845D7002FFC24 /* Demo.entitlements */, + D5B0419F2926AAFF0019620D /* App.swift */, + D5B0419D2926AAFF0019620D /* AppView.swift */, + D5B0419E2926AAFF0019620D /* AppViewController.swift */, + D5934B1729272EF2002FFC24 /* PageScrollView.swift */, + D5B041A92926D5D40019620D /* DRPageScrollView.h */, + D5B041AA2926D5D40019620D /* DRPageScrollView.m */, + D5934B1A29284AE2002FFC24 /* EasingFunctions.swift */, + D5B041A62926AE8A0019620D /* Helpers.swift */, + D5B041932926AA680019620D /* Assets.xcassets */, + D5B041A82926D5D30019620D /* Demo-Bridging-Header.h */, + ); + path = Demo; + sourceTree = ""; + }; + D5B041A32926AB3A0019620D /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D5B0418B2926AA670019620D /* Demo */ = { + isa = PBXNativeTarget; + buildConfigurationList = D5B0419A2926AA680019620D /* Build configuration list for PBXNativeTarget "Demo" */; + buildPhases = ( + D5B041882926AA670019620D /* Sources */, + D5B041892926AA670019620D /* Frameworks */, + D5B0418A2926AA670019620D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Demo; + packageProductDependencies = ( + D5B041A42926AB3A0019620D /* Popsicle */, + ); + productName = Demo; + productReference = D5B0418C2926AA670019620D /* Demo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D5B041842926AA670019620D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1410; + LastUpgradeCheck = 1410; + TargetAttributes = { + D5B0418B2926AA670019620D = { + CreatedOnToolsVersion = 14.1; + LastSwiftMigration = 1410; + }; + }; + }; + buildConfigurationList = D5B041872926AA670019620D /* Build configuration list for PBXProject "Demo" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D5B041832926AA670019620D; + productRefGroup = D5B0418D2926AA670019620D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D5B0418B2926AA670019620D /* Demo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D5B0418A2926AA670019620D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D5B041942926AA680019620D /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D5B041882926AA670019620D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D5934B1829272EF2002FFC24 /* PageScrollView.swift in Sources */, + D5B041A22926AAFF0019620D /* App.swift in Sources */, + D5B041A12926AAFF0019620D /* AppViewController.swift in Sources */, + D5B041A02926AAFF0019620D /* AppView.swift in Sources */, + D5934B1B29284AE2002FFC24 /* EasingFunctions.swift in Sources */, + D5B041A72926AE8A0019620D /* Helpers.swift in Sources */, + D5B041AB2926D5D40019620D /* DRPageScrollView.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D5B041982926AA680019620D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + D5B041992926AA680019620D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + D5B0419B2926AA680019620D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Demo/Demo.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 895A67RTTU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = mn.dro.Demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Demo/Demo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; + }; + name = Debug; + }; + D5B0419C2926AA680019620D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Demo/Demo.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 895A67RTTU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = mn.dro.Demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Demo/Demo-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D5B041872926AA670019620D /* Build configuration list for PBXProject "Demo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D5B041982926AA680019620D /* Debug */, + D5B041992926AA680019620D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D5B0419A2926AA680019620D /* Build configuration list for PBXNativeTarget "Demo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D5B0419B2926AA680019620D /* Debug */, + D5B0419C2926AA680019620D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + D5B041A42926AB3A0019620D /* Popsicle */ = { + isa = XCSwiftPackageProductDependency; + productName = Popsicle; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = D5B041842926AA670019620D /* Project object */; +} diff --git a/Demo/Demo/App.swift b/Demo/Demo/App.swift new file mode 100644 index 0000000..3581141 --- /dev/null +++ b/Demo/Demo/App.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct App: SwiftUI.App { + var body: some Scene { + WindowGroup { + AppView() + } + } +} diff --git a/Demo/Demo/AppView.swift b/Demo/Demo/AppView.swift new file mode 100644 index 0000000..7a61120 --- /dev/null +++ b/Demo/Demo/AppView.swift @@ -0,0 +1,23 @@ +import SwiftUI + +struct AppView: View { + var body: some View { + Content().edgesIgnoringSafeArea(.all) + } +} + +extension AppView { + struct Content: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> some UIViewController { + AppViewController() + } + + func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} + } +} + +struct AppView_Previews: PreviewProvider { + static var previews: some View { + AppView() + } +} diff --git a/Demo/Demo/AppViewController.swift b/Demo/Demo/AppViewController.swift new file mode 100644 index 0000000..b8a9a0f --- /dev/null +++ b/Demo/Demo/AppViewController.swift @@ -0,0 +1,27 @@ +import Popsicle +import UIKit + +final class AppViewController: UIViewController, UIScrollViewDelegate { + + let interpolator = Interpolator() + + override func viewDidLoad() { + super.viewDidLoad() + + PageScrollView(interpolator: interpolator) .. { + $0.delegate = self + view.addSubview($0) + $0.pinToSuperviewEdges() + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + interpolator(0) + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + interpolator(scrollView.contentOffset.x / scrollView.frame.width) + } +} diff --git a/Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demo/Demo/Assets.xcassets/Contents.json b/Demo/Demo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Demo/Demo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demo/Demo/Assets.xcassets/popsicle.imageset/Contents.json b/Demo/Demo/Assets.xcassets/popsicle.imageset/Contents.json new file mode 100644 index 0000000..0381d6a --- /dev/null +++ b/Demo/Demo/Assets.xcassets/popsicle.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "popsicle.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/PopsicleDemo/Assets.xcassets/logo.imageset/logo.pdf b/Demo/Demo/Assets.xcassets/popsicle.imageset/popsicle.pdf old mode 100755 new mode 100644 similarity index 69% rename from PopsicleDemo/Assets.xcassets/logo.imageset/logo.pdf rename to Demo/Demo/Assets.xcassets/popsicle.imageset/popsicle.pdf index 077f3cc..3a73861 Binary files a/PopsicleDemo/Assets.xcassets/logo.imageset/logo.pdf and b/Demo/Demo/Assets.xcassets/popsicle.imageset/popsicle.pdf differ diff --git a/PopsicleDemo/DRPageScrollView.h b/Demo/Demo/DRPageScrollView.h similarity index 86% rename from PopsicleDemo/DRPageScrollView.h rename to Demo/Demo/DRPageScrollView.h index 0cf177a..e7a4085 100755 --- a/PopsicleDemo/DRPageScrollView.h +++ b/Demo/Demo/DRPageScrollView.h @@ -9,7 +9,7 @@ #import /// The type of block used to define what to display on each page view. -typedef void(^DRPageHandlerBlock)(UIView *pageView); +typedef void(^DRPageHandlerBlock)(UIView * _Nonnull pageView); @interface DRPageScrollView : UIScrollView @@ -27,6 +27,6 @@ typedef void(^DRPageHandlerBlock)(UIView *pageView); * * @param handler A block that defines what to display on the page. **/ -- (void)addPageWithHandler:(DRPageHandlerBlock)handler; +- (void)addPageWithHandler:(DRPageHandlerBlock _Nonnull)handler; @end diff --git a/PopsicleDemo/DRPageScrollView.m b/Demo/Demo/DRPageScrollView.m similarity index 99% rename from PopsicleDemo/DRPageScrollView.m rename to Demo/Demo/DRPageScrollView.m index fdfd583..a7f7d08 100755 --- a/PopsicleDemo/DRPageScrollView.m +++ b/Demo/Demo/DRPageScrollView.m @@ -38,7 +38,9 @@ - (instancetype)initWithFrame:(CGRect)frame { - (void)commonInit { previousPage = -1; +#if !TARGET_OS_TV self.pagingEnabled = YES; +#endif self.showsHorizontalScrollIndicator = NO; self.showsVerticalScrollIndicator = NO; diff --git a/Demo/Demo/Demo-Bridging-Header.h b/Demo/Demo/Demo-Bridging-Header.h new file mode 100644 index 0000000..3b13611 --- /dev/null +++ b/Demo/Demo/Demo-Bridging-Header.h @@ -0,0 +1 @@ +#import "DRPageScrollView.h" diff --git a/Demo/Demo/Demo.entitlements b/Demo/Demo/Demo.entitlements new file mode 100644 index 0000000..ee95ab7 --- /dev/null +++ b/Demo/Demo/Demo.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/Demo/Demo/EasingFunctions.swift b/Demo/Demo/EasingFunctions.swift new file mode 100644 index 0000000..0cfd6b2 --- /dev/null +++ b/Demo/Demo/EasingFunctions.swift @@ -0,0 +1,36 @@ +// Adapted from https://gist.github.com/muukii/f79d44b13db2a2004ac091352a3c0b04 + +import UIKit + +extension UITimingCurveProvider where Self == UICubicTimingParameters { + static var easeInSine: Self { .init(0.47, 0, 0.745, 0.715) } + static var easeOutSine: Self { .init(0.39, 0.575, 0.565, 1) } + static var easeInOutSine: Self { .init(0.445, 0.05, 0.55, 0.95) } + static var easeInQuad: Self { .init(0.55, 0.085, 0.68, 0.53) } + static var easeOutQuad: Self { .init(0.25, 0.46, 0.45, 0.94) } + static var easeInOutQuad: Self { .init(0.455, 0.03, 0.515, 0.955) } + static var easeInCubic: Self { .init(0.55, 0.055, 0.675, 0.19) } + static var easeOutCubic: Self { .init(0.215, 0.61, 0.355, 1) } + static var easeInOutCubic: Self { .init(0.645, 0.045, 0.355, 1) } + static var easeInQuart: Self { .init(0.895, 0.03, 0.685, 0.22) } + static var easeOutQuart: Self { .init(0.165, 0.84, 0.44, 1) } + static var easeInOutQuart: Self { .init(0.77, 0, 0.175, 1) } + static var easeInQuint: Self { .init(0.755, 0.05, 0.855, 0.06) } + static var easeOutQuint: Self { .init(0.23, 1, 0.32, 1) } + static var easeInOutQuint: Self { .init(0.86, 0, 0.07, 1) } + static var easeInExpo: Self { .init(0.95, 0.05, 0.795, 0.035) } + static var easeOutExpo: Self { .init(0.19, 1, 0.22, 1) } + static var easeInOutExpo: Self { .init(1, 0, 0, 1) } + static var easeInCirc: Self { .init(0.6, 0.04, 0.98, 0.335) } + static var easeOutCirc: Self { .init(0.075, 0.82, 0.165, 1) } + static var easeInOutCirc: Self { .init(0.785, 0.135, 0.15, 0.86) } + static var easeInBack: Self { .init(0.6, -0.28, 0.735, 0.045) } + static var easeOutBack: Self { .init(0.175, 0.885, 0.32, 1.275) } + static var easeInOutBack: Self { .init(0.68, -0.55, 0.265, 1.55) } +} + +fileprivate extension UICubicTimingParameters { + convenience init(_ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat) { + self.init(controlPoint1: .init(x: x1, y: y1), controlPoint2: .init(x: x2, y: y2)) + } +} diff --git a/Demo/Demo/Helpers.swift b/Demo/Demo/Helpers.swift new file mode 100644 index 0000000..9e7d267 --- /dev/null +++ b/Demo/Demo/Helpers.swift @@ -0,0 +1,34 @@ +import UIKit + +infix operator .. + +extension UIView { + convenience init(transform: (Self) -> Void) { + self.init() + transform(self) + } +} + +@discardableResult +func .. ( + view: View, + transform: (View) -> Void +) -> View { + transform(view) + return view +} + +extension UIView { + func pinToSuperviewEdges() { + guard let superview else { return } + + self.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + self.topAnchor.constraint(equalTo: superview.topAnchor), + self.leadingAnchor.constraint(equalTo: superview.leadingAnchor), + self.trailingAnchor.constraint(equalTo: superview.trailingAnchor), + self.bottomAnchor.constraint(equalTo: superview.bottomAnchor), + ]) + } +} diff --git a/Demo/Demo/PageScrollView.swift b/Demo/Demo/PageScrollView.swift new file mode 100644 index 0000000..e8d1ed4 --- /dev/null +++ b/Demo/Demo/PageScrollView.swift @@ -0,0 +1,198 @@ +import Popsicle +import UIKit +import struct SwiftUI.Angle + +final class PageScrollView: DRPageScrollView { + init(interpolator: Interpolator) { + super.init(frame: .zero) + + let firstPageView = FirstPageView(in: self, interpolator: interpolator) + let secondPageView = SecondPageView(in: self, interpolator: interpolator) + let thirdPageView = ThirdPageView(in: self, interpolator: interpolator) + let fourthPageView = FourthPageView(in: self, interpolator: interpolator) + + for page in [firstPageView, secondPageView, thirdPageView, fourthPageView] { + self.addPage { view in + view.addSubview(page) + page.pinToSuperviewEdges() + } + } + } + + @available(*, unavailable) required init?(coder: NSCoder) { fatalError() } +} + +class PageView: UIView { + init() { + super.init(frame: .zero) + } + + @available(*, unavailable) required init?(coder: NSCoder) { fatalError() } +} + +final class FirstPageView: PageView { + init(in scrollView: PageScrollView, interpolator: Interpolator) { + super.init() + + let image = UIImage(named: "popsicle")! + let popsicle = UIImageView(image: image) .. { + $0.contentMode = .scaleAspectFit + } + addSubview(popsicle) + popsicle.translatesAutoresizingMaskIntoConstraints = false + + let centerXConstraint = popsicle.centerXAnchor.constraint(equalTo: centerXAnchor) + let centerYConstraint = popsicle.centerYAnchor.constraint(equalTo: centerYAnchor) + let bottomConstraint = popsicle.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 50) + + NSLayoutConstraint.activate([ + centerXConstraint, + popsicle.widthAnchor.constraint(equalToConstant: image.size.width / 1.5), + popsicle.heightAnchor.constraint(equalToConstant: image.size.height / 1.5), + ]) + + interpolator.addKeyframe(0) { + scrollView.backgroundColor = .white + popsicle.transform = .identity + popsicle.alpha = 1 + } + + interpolator.addKeyframe(0) { + centerXConstraint.constant = 0 + centerYConstraint.isActive = true + bottomConstraint.isActive = false + self.layoutIfNeeded() + } + + interpolator.addKeyframe(1, .easeInQuad) { + centerXConstraint.constant = self.frame.width + centerYConstraint.isActive = false + bottomConstraint.isActive = true + self.layoutIfNeeded() + } + + interpolator.addKeyframe(2, .easeInQuad) { + centerXConstraint.constant = self.frame.width * 2 + centerYConstraint.isActive = true + bottomConstraint.isActive = false + self.layoutIfNeeded() + } + +// interpolator.addInterpolation(from: 1.7, to: 2) { +// scrollView.backgroundColor = .orange +// } + + interpolator.addKeyframe(2) { + popsicle.transform = .identity.rotated(by: Angle(degrees: 45).radians) + popsicle.alpha = 0.3 + } + } +} + +final class SecondPageView: PageView { + init(in scrollView: PageScrollView, interpolator: Interpolator) { + super.init() + + interpolator.addKeyframe(1) { +// scrollView.backgroundColor = .purple + } + } +} + +final class ThirdPageView: PageView { + init(in scrollView: PageScrollView, interpolator: Interpolator) { + super.init() + + interpolator.addKeyframe(2) { +// scrollView.backgroundColor = .green + } + } +} + +final class FourthPageView: PageView { + init(in scrollView: PageScrollView, interpolator: Interpolator) { + super.init() + + interpolator.addKeyframe(3) { +// scrollView.backgroundColor = .red + } + } +} + + +// override func viewDidLayoutSubviews() { +// super.viewDidLayoutSubviews() +// +// self.interpolator.removeAllInterpolations() +// +// let backgroundColorInterpolation = Interpolation(self.view, backgroundColor) +// backgroundColorInterpolation[1, 3] = UIColor.whiteColor() +// backgroundColorInterpolation[1.7, 2] = UIColor(red: 254/255, green: 134/255, blue: 44/255, alpha: 1) +// self.interpolator.addInterpolation(backgroundColorInterpolation) +// +// let barTintColorInterpolation = Interpolation(self.navigationController!.navigationBar, barTintColor) +// barTintColorInterpolation[1, 3] = UIColor.whiteColor() +// barTintColorInterpolation[1.7, 2] = UIColor(red: 244255, green: 219/255, blue: 165/255, alpha: 1) +// self.interpolator.addInterpolation(barTintColorInterpolation) +// +// if let imageView = self.pageScrollView.firstPageView.imageView { +// let xInterpolation = Interpolation(imageView, centerXConstraint) +// xInterpolation[0] = 0 +// xInterpolation.setEasingFunction(EasingFunctionEaseInQuad, forTime: 0) +// xInterpolation[1] = -self.pageScrollView.frame.width +// xInterpolation[2] = -self.pageScrollView.frame.width*2 +// xInterpolation[3] = -self.pageScrollView.frame.width*3 +// self.interpolator.addInterpolation(xInterpolation) +// +// let yInterpolation = Interpolation(imageView, centerYConstraint) +// yInterpolation[0] = 0 +// yInterpolation[1, 2] = -self.pageScrollView.frame.height/2+80 +// yInterpolation[3] = 0 +// self.interpolator.addInterpolation(yInterpolation) +// +// let alphaInterpolation = Interpolation(imageView, alpha) +// alphaInterpolation[1] = 1 +// alphaInterpolation[2] = 0 +// alphaInterpolation[3] = 0.25 +// self.interpolator.addInterpolation(alphaInterpolation) +// +// let transformInterpolation = Interpolation(imageView, transform) +// transformInterpolation[0, 1, 2] = CGAffineTransformIdentity +// transformInterpolation[0.25] = CGAffineTransformMake(0, 0, 1.1, 1.1, 0) +// transformInterpolation[3] = CGAffineTransformMake(0, 0, 1.4, 1.4, 60) +// self.interpolator.addInterpolation(transformInterpolation) +// } +// +// if let label1 = self.pageScrollView.firstPageView.label { +// let alphaInterpolation = Interpolation(label1, alpha) +// alphaInterpolation[0] = 1 +// alphaInterpolation[0.4] = 0 +// self.interpolator.addInterpolation(alphaInterpolation) +// } +// +// if let label2 = self.pageScrollView.secondPageView.label { +// let scaleInterpolation = Interpolation(label2, transform) +// scaleInterpolation[0] = CGAffineTransformMake(0, 0, 0.6, 0.6, 0) +// scaleInterpolation[1] = CGAffineTransformMake(0, 0, 1, 1, 0) +// self.interpolator.addInterpolation(scaleInterpolation) +// +// let alphaInterpolation = Interpolation(label2, alpha) +// alphaInterpolation[1] = 1 +// alphaInterpolation[1.7] = 0 +// self.interpolator.addInterpolation(alphaInterpolation) +// } +// +// if let label3 = self.pageScrollView.thirdPageView.label1, let label4 = self.pageScrollView.thirdPageView.label2 { +// let translateInterpolation1 = Interpolation(label3, transform) +// translateInterpolation1[1] = CGAffineTransformMake(100, 0, 1, 1, 0) +// translateInterpolation1[2] = CGAffineTransformIdentity +// translateInterpolation1[3] = CGAffineTransformMake(-100, 0, 1, 1, 0) +// self.interpolator.addInterpolation(translateInterpolation1) +// +// let translateInterpolation2 = Interpolation(label4, transform) +// translateInterpolation2[1] = CGAffineTransformMake(300, 0, 1, 1, 0) +// translateInterpolation2[2] = CGAffineTransformIdentity +// translateInterpolation2[3] = CGAffineTransformMake(-300, 0, 1, 1, 0) +// self.interpolator.addInterpolation(translateInterpolation2) +// } +// } diff --git a/LICENSE b/LICENSE deleted file mode 100755 index d8ebf61..0000000 --- a/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 IFTTT Inc -Copyright (c) 2015 David Román - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..37d748c --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections", + "state": { + "branch": "main", + "revision": "0d4b9e3346eb0373ac6f85fe799b44b9b7e86d7e", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..0317868 --- /dev/null +++ b/Package.swift @@ -0,0 +1,34 @@ +// swift-tools-version: 5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Popsicle", + platforms: [ + .iOS(.v11), + .macCatalyst(.v13), + .tvOS(.v11), + ], + products: [ + .library(name: "Popsicle", targets: ["Popsicle"]), + ], + targets: [ + .target( + name: "Popsicle", + dependencies: [ + .product(name: "SortedCollections", package: "swift-collections") + ] + ), + .testTarget( + name: "PopsicleTests", + dependencies: [ + "Popsicle", + ] + ), + ] +) + +package.dependencies = [ + .package(url: "https://github.com/apple/swift-collections", branch: "main"), +] diff --git a/Popsicle.podspec b/Popsicle.podspec deleted file mode 100755 index f6a5865..0000000 --- a/Popsicle.podspec +++ /dev/null @@ -1,17 +0,0 @@ -Pod::Spec.new do |s| - s.name = "Popsicle" - s.version = "2.0.1" - s.summary = "Delightful, extensible Swift value interpolation framework" - s.homepage = "https://github.com/DavdRoman/Popsicle" - s.author = { "David Román" => "d@vidroman.me" } - s.license = { :type => 'MIT', :file => 'LICENSE' } - s.social_media_url = 'https://twitter.com/DavdRoman' - - s.platform = :ios, '8.0' - s.ios.deployment_target = '8.0' - - s.source = { :git => "https://github.com/DavdRoman/Popsicle.git", :tag => s.version.to_s } - s.source_files = 'Popsicle/*.{h,swift}' - s.frameworks = 'UIKit' - s.requires_arc = true -end diff --git a/Popsicle.xcodeproj/project.pbxproj b/Popsicle.xcodeproj/project.pbxproj deleted file mode 100755 index 1d0fdc1..0000000 --- a/Popsicle.xcodeproj/project.pbxproj +++ /dev/null @@ -1,507 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - D53B324C1BE65FF800A1820B /* Interpolable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53B324B1BE65FF800A1820B /* Interpolable.swift */; }; - D53E5FD91BEA12340043DF84 /* EasingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FD81BEA12340043DF84 /* EasingFunction.swift */; }; - D53E5FDB1BEA13600043DF84 /* Interpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FDA1BEA13600043DF84 /* Interpolation.swift */; }; - D53E5FDD1BEA13900043DF84 /* Interpolator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FDC1BEA13900043DF84 /* Interpolator.swift */; }; - D53E5FE51BEA145F0043DF84 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FE41BEA145F0043DF84 /* AppDelegate.swift */; }; - D53E5FE71BEA145F0043DF84 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FE61BEA145F0043DF84 /* ViewController.swift */; }; - D53E5FEC1BEA145F0043DF84 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53E5FEB1BEA145F0043DF84 /* Assets.xcassets */; }; - D53E5FEF1BEA145F0043DF84 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D53E5FED1BEA145F0043DF84 /* LaunchScreen.storyboard */; }; - D53E5FF71BEA16D70043DF84 /* PageViews.xib in Resources */ = {isa = PBXBuildFile; fileRef = D53E5FF61BEA16D70043DF84 /* PageViews.xib */; }; - D53E60011BEA203B0043DF84 /* PageScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FFF1BEA199C0043DF84 /* PageScrollView.swift */; }; - D53E60021BEA227D0043DF84 /* DRPageScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = D53E5FFA1BEA18140043DF84 /* DRPageScrollView.m */; }; - D53E60051BEA67790043DF84 /* UIView+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53E60041BEA67790043DF84 /* UIView+Utils.swift */; }; - D593A8DD1BEA72E500AFA257 /* Popsicle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5FBF53A1BE65BB500F3CB79 /* Popsicle.framework */; }; - D593A8DE1BEA72E500AFA257 /* Popsicle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D5FBF53A1BE65BB500F3CB79 /* Popsicle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D5D6A94C1BEBB85A008E414E /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = D593A8E41BEA88B600AFA257 /* KeyPath.swift */; }; - D5FBF53E1BE65BB500F3CB79 /* Popsicle.h in Headers */ = {isa = PBXBuildFile; fileRef = D5FBF53D1BE65BB500F3CB79 /* Popsicle.h */; settings = {ATTRIBUTES = (Public, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - D593A8DF1BEA72E500AFA257 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D5FBF5311BE65BB500F3CB79 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D5FBF5391BE65BB500F3CB79; - remoteInfo = Popsicle; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - D593A8E11BEA72E500AFA257 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - D593A8DE1BEA72E500AFA257 /* Popsicle.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - D53B324B1BE65FF800A1820B /* Interpolable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interpolable.swift; sourceTree = ""; }; - D53E5FD81BEA12340043DF84 /* EasingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasingFunction.swift; sourceTree = ""; }; - D53E5FDA1BEA13600043DF84 /* Interpolation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interpolation.swift; sourceTree = ""; }; - D53E5FDC1BEA13900043DF84 /* Interpolator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interpolator.swift; sourceTree = ""; }; - D53E5FE21BEA145F0043DF84 /* PopsicleDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PopsicleDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - D53E5FE41BEA145F0043DF84 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - D53E5FE61BEA145F0043DF84 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - D53E5FEB1BEA145F0043DF84 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - D53E5FEE1BEA145F0043DF84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - D53E5FF01BEA145F0043DF84 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D53E5FF61BEA16D70043DF84 /* PageViews.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PageViews.xib; sourceTree = ""; }; - D53E5FF81BEA18130043DF84 /* PopsicleDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PopsicleDemo-Bridging-Header.h"; sourceTree = ""; }; - D53E5FF91BEA18140043DF84 /* DRPageScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DRPageScrollView.h; sourceTree = ""; }; - D53E5FFA1BEA18140043DF84 /* DRPageScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DRPageScrollView.m; sourceTree = ""; }; - D53E5FFF1BEA199C0043DF84 /* PageScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageScrollView.swift; sourceTree = ""; }; - D53E60041BEA67790043DF84 /* UIView+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Utils.swift"; sourceTree = ""; }; - D593A8E41BEA88B600AFA257 /* KeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = ""; }; - D5FBF53A1BE65BB500F3CB79 /* Popsicle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Popsicle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D5FBF53D1BE65BB500F3CB79 /* Popsicle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Popsicle.h; sourceTree = ""; }; - D5FBF53F1BE65BB500F3CB79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - D53E5FDF1BEA145F0043DF84 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D593A8DD1BEA72E500AFA257 /* Popsicle.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D5FBF5361BE65BB500F3CB79 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - D516D8DF1D1A90910086E8E6 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D53E5FEB1BEA145F0043DF84 /* Assets.xcassets */, - D53E5FED1BEA145F0043DF84 /* LaunchScreen.storyboard */, - D53E5FF01BEA145F0043DF84 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D53E5FE31BEA145F0043DF84 /* PopsicleDemo */ = { - isa = PBXGroup; - children = ( - D53E5FE41BEA145F0043DF84 /* AppDelegate.swift */, - D53E5FE61BEA145F0043DF84 /* ViewController.swift */, - D5F2E3E11BEB720B00008043 /* Page Scroll View */, - D516D8DF1D1A90910086E8E6 /* Supporting Files */, - ); - path = PopsicleDemo; - sourceTree = ""; - }; - D5F2E3E11BEB720B00008043 /* Page Scroll View */ = { - isa = PBXGroup; - children = ( - D53E5FF81BEA18130043DF84 /* PopsicleDemo-Bridging-Header.h */, - D53E5FF91BEA18140043DF84 /* DRPageScrollView.h */, - D53E5FFA1BEA18140043DF84 /* DRPageScrollView.m */, - D53E5FFF1BEA199C0043DF84 /* PageScrollView.swift */, - D53E5FF61BEA16D70043DF84 /* PageViews.xib */, - D53E60041BEA67790043DF84 /* UIView+Utils.swift */, - ); - name = "Page Scroll View"; - sourceTree = ""; - }; - D5FBF5301BE65BB500F3CB79 = { - isa = PBXGroup; - children = ( - D53E5FE31BEA145F0043DF84 /* PopsicleDemo */, - D5FBF53C1BE65BB500F3CB79 /* Popsicle */, - D5FBF53B1BE65BB500F3CB79 /* Products */, - ); - sourceTree = ""; - }; - D5FBF53B1BE65BB500F3CB79 /* Products */ = { - isa = PBXGroup; - children = ( - D5FBF53A1BE65BB500F3CB79 /* Popsicle.framework */, - D53E5FE21BEA145F0043DF84 /* PopsicleDemo.app */, - ); - name = Products; - sourceTree = ""; - }; - D5FBF53C1BE65BB500F3CB79 /* Popsicle */ = { - isa = PBXGroup; - children = ( - D5FBF53D1BE65BB500F3CB79 /* Popsicle.h */, - D53E5FDC1BEA13900043DF84 /* Interpolator.swift */, - D53E5FDA1BEA13600043DF84 /* Interpolation.swift */, - D53B324B1BE65FF800A1820B /* Interpolable.swift */, - D53E5FD81BEA12340043DF84 /* EasingFunction.swift */, - D593A8E41BEA88B600AFA257 /* KeyPath.swift */, - D5FBF53F1BE65BB500F3CB79 /* Info.plist */, - ); - path = Popsicle; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - D5FBF5371BE65BB500F3CB79 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D5FBF53E1BE65BB500F3CB79 /* Popsicle.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - D53E5FE11BEA145F0043DF84 /* PopsicleDemo */ = { - isa = PBXNativeTarget; - buildConfigurationList = D53E5FF11BEA145F0043DF84 /* Build configuration list for PBXNativeTarget "PopsicleDemo" */; - buildPhases = ( - D53E5FDE1BEA145F0043DF84 /* Sources */, - D53E5FDF1BEA145F0043DF84 /* Frameworks */, - D53E5FE01BEA145F0043DF84 /* Resources */, - D593A8E11BEA72E500AFA257 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D593A8E01BEA72E500AFA257 /* PBXTargetDependency */, - ); - name = PopsicleDemo; - productName = PopsicleDemo; - productReference = D53E5FE21BEA145F0043DF84 /* PopsicleDemo.app */; - productType = "com.apple.product-type.application"; - }; - D5FBF5391BE65BB500F3CB79 /* Popsicle */ = { - isa = PBXNativeTarget; - buildConfigurationList = D5FBF54E1BE65BB600F3CB79 /* Build configuration list for PBXNativeTarget "Popsicle" */; - buildPhases = ( - D5FBF5351BE65BB500F3CB79 /* Sources */, - D5FBF5361BE65BB500F3CB79 /* Frameworks */, - D5FBF5371BE65BB500F3CB79 /* Headers */, - D5FBF5381BE65BB500F3CB79 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Popsicle; - productName = Popsicle; - productReference = D5FBF53A1BE65BB500F3CB79 /* Popsicle.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D5FBF5311BE65BB500F3CB79 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0710; - ORGANIZATIONNAME = "David Román Aguirre"; - TargetAttributes = { - D53E5FE11BEA145F0043DF84 = { - CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 0800; - }; - D5FBF5391BE65BB500F3CB79 = { - CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 0800; - }; - }; - }; - buildConfigurationList = D5FBF5341BE65BB500F3CB79 /* Build configuration list for PBXProject "Popsicle" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = D5FBF5301BE65BB500F3CB79; - productRefGroup = D5FBF53B1BE65BB500F3CB79 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D53E5FE11BEA145F0043DF84 /* PopsicleDemo */, - D5FBF5391BE65BB500F3CB79 /* Popsicle */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - D53E5FE01BEA145F0043DF84 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D53E5FEF1BEA145F0043DF84 /* LaunchScreen.storyboard in Resources */, - D53E5FEC1BEA145F0043DF84 /* Assets.xcassets in Resources */, - D53E5FF71BEA16D70043DF84 /* PageViews.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D5FBF5381BE65BB500F3CB79 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - D53E5FDE1BEA145F0043DF84 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D53E5FE71BEA145F0043DF84 /* ViewController.swift in Sources */, - D53E5FE51BEA145F0043DF84 /* AppDelegate.swift in Sources */, - D53E60051BEA67790043DF84 /* UIView+Utils.swift in Sources */, - D53E60021BEA227D0043DF84 /* DRPageScrollView.m in Sources */, - D53E60011BEA203B0043DF84 /* PageScrollView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D5FBF5351BE65BB500F3CB79 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D53E5FDD1BEA13900043DF84 /* Interpolator.swift in Sources */, - D5D6A94C1BEBB85A008E414E /* KeyPath.swift in Sources */, - D53E5FD91BEA12340043DF84 /* EasingFunction.swift in Sources */, - D53B324C1BE65FF800A1820B /* Interpolable.swift in Sources */, - D53E5FDB1BEA13600043DF84 /* Interpolation.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - D593A8E01BEA72E500AFA257 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D5FBF5391BE65BB500F3CB79 /* Popsicle */; - targetProxy = D593A8DF1BEA72E500AFA257 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - D53E5FED1BEA145F0043DF84 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - D53E5FEE1BEA145F0043DF84 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - D53E5FF21BEA145F0043DF84 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - INFOPLIST_FILE = PopsicleDemo/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.davidroman.PopsicleDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "PopsicleDemo/PopsicleDemo-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; - }; - name = Debug; - }; - D53E5FF31BEA145F0043DF84 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - INFOPLIST_FILE = PopsicleDemo/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.davidroman.PopsicleDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "PopsicleDemo/PopsicleDemo-Bridging-Header.h"; - SWIFT_VERSION = 2.3; - }; - name = Release; - }; - D5FBF54C1BE65BB600F3CB79 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - D5FBF54D1BE65BB600F3CB79 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - D5FBF54F1BE65BB600F3CB79 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Popsicle/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.davidroman.Popsicle; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; - }; - name = Debug; - }; - D5FBF5501BE65BB600F3CB79 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Popsicle/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.davidroman.Popsicle; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 2.3; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - D53E5FF11BEA145F0043DF84 /* Build configuration list for PBXNativeTarget "PopsicleDemo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D53E5FF21BEA145F0043DF84 /* Debug */, - D53E5FF31BEA145F0043DF84 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D5FBF5341BE65BB500F3CB79 /* Build configuration list for PBXProject "Popsicle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D5FBF54C1BE65BB600F3CB79 /* Debug */, - D5FBF54D1BE65BB600F3CB79 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D5FBF54E1BE65BB600F3CB79 /* Build configuration list for PBXNativeTarget "Popsicle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D5FBF54F1BE65BB600F3CB79 /* Debug */, - D5FBF5501BE65BB600F3CB79 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D5FBF5311BE65BB500F3CB79 /* Project object */; -} diff --git a/Popsicle.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Popsicle.xcworkspace/contents.xcworkspacedata old mode 100755 new mode 100644 similarity index 52% rename from Popsicle.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Popsicle.xcworkspace/contents.xcworkspacedata index 324ddd8..ed63a35 --- a/Popsicle.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Popsicle.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,9 @@ + location = "group:Demo/Demo.xcodeproj"> + + diff --git a/Popsicle.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Popsicle.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Popsicle.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Popsicle.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Popsicle.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..37d748c --- /dev/null +++ b/Popsicle.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections", + "state": { + "branch": "main", + "revision": "0d4b9e3346eb0373ac6f85fe799b44b9b7e86d7e", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/Popsicle.xcodeproj/xcshareddata/xcschemes/Popsicle.xcscheme b/Popsicle.xcworkspace/xcshareddata/xcschemes/Popsicle.xcscheme old mode 100755 new mode 100644 similarity index 61% rename from Popsicle.xcodeproj/xcshareddata/xcschemes/Popsicle.xcscheme rename to Popsicle.xcworkspace/xcshareddata/xcschemes/Popsicle.xcscheme index 4795af9..a364c99 --- a/Popsicle.xcodeproj/xcshareddata/xcschemes/Popsicle.xcscheme +++ b/Popsicle.xcworkspace/xcshareddata/xcschemes/Popsicle.xcscheme @@ -1,6 +1,6 @@ + ReferencedContainer = "container:"> @@ -32,24 +32,13 @@ skipped = "NO"> + ReferencedContainer = "container:"> - - - - - - - - - - - - + ReferencedContainer = "container:"> diff --git a/Popsicle/EasingFunction.swift b/Popsicle/EasingFunction.swift deleted file mode 100755 index 16d438a..0000000 --- a/Popsicle/EasingFunction.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// EasingFunction.swift -// RazzleDazzle -// -// Created by Laura Skelton on 6/15/15. -// Copyright (c) 2015 IFTTT. All rights reserved. -// - -// Ported to Swift from Robert Böhnke's RBBAnimation, original available here: -// - -public typealias EasingFunction = (Progress) -> (Progress) - -public let EasingFunctionLinear: EasingFunction = { t in - return t -} - -public let EasingFunctionEaseInQuad: EasingFunction = { t in - return t * t -} - -public let EasingFunctionEaseOutQuad: EasingFunction = { t in - return t * (2 - t) -} - -public let EasingFunctionEaseInOutQuad: EasingFunction = { t in - if (t < 0.5) { return 2 * t * t } - return -1 + ((4 - (2 * t)) * t) -} - -public let EasingFunctionEaseInCubic: EasingFunction = { t in - return t * t * t -} - -public let EasingFunctionEaseOutCubic: EasingFunction = { t in - return pow(t - 1, 3) + 1 -} - -public let EasingFunctionEaseInOutCubic: EasingFunction = { t in - if (t < 0.5) { return 4 * pow(t, 3) } - return ((t - 1) * pow((2 * t) - 2, 2)) + 1 -} - -public let EasingFunctionEaseInBounce: EasingFunction = { t in - return 1 - EasingFunctionEaseOutBounce(1 - t) -} - -public let EasingFunctionEaseOutBounce: EasingFunction = { t in - if (t < (4.0 / 11.0)) { - return pow((11.0 / 4.0), 2) * pow(t, 2) - } - - if (t < (8.0 / 11.0)) { - return (3.0 / 4.0) + (pow((11.0 / 4.0), 2) * pow(t - (6.0 / 11.0), 2)) - } - - if (t < (10.0 / 11.0)) { - return (15.0 / 16.0) + (pow((11.0 / 4.0), 2) * pow(t - (9.0 / 11.0), 2)) - } - - return (63.0 / 64.0) + (pow((11.0 / 4.0), 2) * pow(t - (21.0 / 22.0), 2)) -} \ No newline at end of file diff --git a/Popsicle/Info.plist b/Popsicle/Info.plist deleted file mode 100755 index 783e22e..0000000 --- a/Popsicle/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 2.0.1 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Popsicle/Interpolable.swift b/Popsicle/Interpolable.swift deleted file mode 100755 index d231c00..0000000 --- a/Popsicle/Interpolable.swift +++ /dev/null @@ -1,161 +0,0 @@ -// -// Interpolable.swift -// Popsicle -// -// Created by David Román Aguirre on 01/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -/// A value from 0 to 1 defining the progress of a certain interpolation between two `Interpolable` values. -public typealias Progress = Double - -/// Defines a common protocol for all interpolable values. -/// -/// Types conforming to this protocol are available to use as a `Interpolation` generic type. -public protocol Interpolable { - typealias ValueType - - /// Defines how an interpolation should be performed between two given values of the type conforming to this protocol. - static func interpolate(from fromValue: ValueType, to toValue: ValueType, withProgress: Progress) -> ValueType - - /// Converts a value to a valid `Foundation` object, if necessary. - static func objectify(value: ValueType) -> AnyObject -} - -extension Bool: Interpolable { - public static func interpolate(from fromValue: Bool, to toValue: Bool, withProgress progress: Progress) -> Bool { - return progress >= 0.5 ? toValue : fromValue - } - - public static func objectify(value: Bool) -> AnyObject { - return NSNumber(bool: value) - } -} - -extension CGFloat: Interpolable { - public static func interpolate(from fromValue: CGFloat, to toValue: CGFloat, withProgress progress: Progress) -> CGFloat { - return fromValue+(toValue-fromValue)*CGFloat(progress) - } - - public static func objectify(value: CGFloat) -> AnyObject { - return NSNumber(double: Double(value)) - } -} - -extension CGPoint: Interpolable { - public static func interpolate(from fromValue: CGPoint, to toValue: CGPoint, withProgress progress: Progress) -> CGPoint { - let x = CGFloat.interpolate(from: fromValue.x, to: toValue.x, withProgress: progress) - let y = CGFloat.interpolate(from: fromValue.y, to: toValue.y, withProgress: progress) - - return CGPointMake(x, y) - } - - public static func objectify(value: CGPoint) -> AnyObject { - return NSValue(CGPoint: value) - } -} - -extension CGSize: Interpolable { - public static func interpolate(from fromValue: CGSize, to toValue: CGSize, withProgress progress: Progress) -> CGSize { - let width = CGFloat.interpolate(from: fromValue.width, to: toValue.width, withProgress: progress) - let height = CGFloat.interpolate(from: fromValue.height, to: toValue.height, withProgress: progress) - - return CGSizeMake(width, height) - } - - public static func objectify(value: CGSize) -> AnyObject { - return NSValue(CGSize: value) - } -} - -extension CGRect: Interpolable { - public static func interpolate(from fromValue: CGRect, to toValue: CGRect, withProgress progress: Progress) -> CGRect { - let origin = CGPoint.interpolate(from: fromValue.origin, to: toValue.origin, withProgress: progress) - let size = CGSize.interpolate(from: fromValue.size, to: toValue.size, withProgress: progress) - - return CGRectMake(origin.x, origin.y, size.width, size.height) - } - - public static func objectify(value: CGRect) -> AnyObject { - return NSValue(CGRect: value) - } -} - -extension CGAffineTransform: Interpolable { - public static func interpolate(from fromValue: CGAffineTransform, to toValue: CGAffineTransform, withProgress progress: Progress) -> CGAffineTransform { - let tx1 = CGAffineTransformGetTranslationX(fromValue) - let tx2 = CGAffineTransformGetTranslationX(toValue) - let tx = CGFloat.interpolate(from: tx1, to: tx2, withProgress: progress) - - let ty1 = CGAffineTransformGetTranslationY(fromValue) - let ty2 = CGAffineTransformGetTranslationY(toValue) - let ty = CGFloat.interpolate(from: ty1, to: ty2, withProgress: progress) - - let sx1 = CGAffineTransformGetScaleX(fromValue) - let sx2 = CGAffineTransformGetScaleX(toValue) - let sx = CGFloat.interpolate(from: sx1, to: sx2, withProgress: progress) - - let sy1 = CGAffineTransformGetScaleY(fromValue) - let sy2 = CGAffineTransformGetScaleY(toValue) - let sy = CGFloat.interpolate(from: sy1, to: sy2, withProgress: progress) - - let deg1 = CGAffineTransformGetRotation(fromValue) - let deg2 = CGAffineTransformGetRotation(toValue) - let deg = CGFloat.interpolate(from: deg1, to: deg2, withProgress: progress) - - return CGAffineTransformMake(tx, ty, sx, sy, deg) - } - - public static func objectify(value: CGAffineTransform) -> AnyObject { - return NSValue(CGAffineTransform: value) - } -} - -/// `CGAffineTransformMake()`, The Right Way™ -/// -/// - parameter tx: translation on x axis. -/// - parameter ty: translation on y axis. -/// - parameter sx: scale factor for width. -/// - parameter sy: scale factor for height. -/// - parameter deg: degrees. -public func CGAffineTransformMake(tx: CGFloat, _ ty: CGFloat, _ sx: CGFloat, _ sy: CGFloat, _ deg: CGFloat) -> CGAffineTransform { - let translationTransform = CGAffineTransformMakeTranslation(tx, ty) - let scaleTransform = CGAffineTransformMakeScale(sx, sy) - let rotationTransform = CGAffineTransformMakeRotation(deg*CGFloat(M_PI_2)/180) - - return CGAffineTransformConcat(CGAffineTransformConcat(translationTransform, scaleTransform), rotationTransform) -} - -func CGAffineTransformGetTranslationX(t: CGAffineTransform) -> CGFloat { return t.tx } -func CGAffineTransformGetTranslationY(t: CGAffineTransform) -> CGFloat { return t.ty } -func CGAffineTransformGetScaleX(t: CGAffineTransform) -> CGFloat { return sqrt(t.a * t.a + t.c * t.c) } -func CGAffineTransformGetScaleY(t: CGAffineTransform) -> CGFloat { return sqrt(t.b * t.b + t.d * t.d) } -func CGAffineTransformGetRotation(t: CGAffineTransform) -> CGFloat { return (atan2(t.b, t.a)*180)/CGFloat(M_PI_2) } - -extension UIColor: Interpolable { - public static func interpolate(from fromValue: UIColor, to toValue: UIColor, withProgress progress: Progress) -> UIColor { - var fromRed: CGFloat = 0 - var fromGreen: CGFloat = 0 - var fromBlue: CGFloat = 0 - var fromAlpha: CGFloat = 0 - - var toRed: CGFloat = 0 - var toGreen: CGFloat = 0 - var toBlue: CGFloat = 0 - var toAlpha: CGFloat = 0 - - fromValue.getRed(&fromRed, green: &fromGreen, blue: &fromBlue, alpha: &fromAlpha) - toValue.getRed(&toRed, green: &toGreen, blue: &toBlue, alpha: &toAlpha) - - let red = CGFloat.interpolate(from: fromRed, to: toRed, withProgress: progress) - let green = CGFloat.interpolate(from: fromGreen, to: toGreen, withProgress: progress) - let blue = CGFloat.interpolate(from: fromBlue, to: toBlue, withProgress: progress) - let alpha = CGFloat.interpolate(from: fromAlpha, to: toAlpha, withProgress: progress) - - return UIColor(red: red, green: green, blue: blue, alpha: alpha) - } - - public static func objectify(value: UIColor) -> AnyObject { - return value - } -} \ No newline at end of file diff --git a/Popsicle/Interpolation.swift b/Popsicle/Interpolation.swift deleted file mode 100755 index 2e2de04..0000000 --- a/Popsicle/Interpolation.swift +++ /dev/null @@ -1,134 +0,0 @@ -// -// Interpolation.swift -// Popsicle -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -protocol ObjectReferable { - var objectReference: NSObject { get } -} - -protocol Timeable { - func setTime(time: Time) -} - -/// `Interpolation` defines an interpolation which changes some `NSObject` value given by a key path. -public class Interpolation : Equatable, ObjectReferable, Timeable { - let object: NSObject - let keyPath: String - - let originalObject: NSObject - var objectReference: NSObject { return self.originalObject } - - typealias Pole = (T.ValueType, EasingFunction) - private var poles: [Time: Pole] = [:] - - public init(_ object: U, _ keyPath: KeyPath) { - self.originalObject = object - (self.object, self.keyPath) = NSObject.filteredObjectAndKeyPath(withObject: object, andKeyPath: keyPath) - - if !self.object.respondsToSelector(NSSelectorFromString(self.keyPath)) { - assertionFailure("Please make sure the key path \"" + self.keyPath + "\" you're referring to for an object of type <" + NSStringFromClass(self.object.dynamicType) + "> is invalid") - } - } - - /// A convenience initializer with `keyPath` as a `String` parameter. You should try to avoid this method unless absolutely necessary, due to its unsafety. - public convenience init(_ object: NSObject, _ keyPath: String) { - self.init(object, KeyPath(keyPathable: keyPath)) - } - - /// Sets a specific easing function for the interpolation to be performed with for a given time. - /// - /// - parameter easingFunction: the easing function to use. - /// - parameter time: the time where the easing function should be used. - public func setEasingFunction(easingFunction: EasingFunction, forTime time: Time) { - self.poles[time]?.1 = easingFunction - } - - public subscript(time1: Time, rest: Time...) -> T.ValueType? { - get { - assert(poles.count >= 2, "You must specify at least 2 poles for an interpolation to be performed") - if let existingPole = poles[time1] { - return existingPole.0 - } else if let timeInterval = poles.keys.sort().elementsAround(time1) { - - guard let fromTime = timeInterval.0 else { - return poles[timeInterval.1!]!.0 - } - - guard let toTime = timeInterval.1 else { - return poles[timeInterval.0!]!.0 - } - - let easingFunction = poles[fromTime]!.1 - let progress = easingFunction(self.progress(fromTime, toTime, time1)) - return T.interpolate(from: poles[fromTime]!.0, to: poles[toTime]!.0, withProgress: progress) - } - - return nil - } - - set { - var times = [time1] - times.appendContentsOf(rest) - for time in times { - poles[time] = (newValue!, EasingFunctionLinear) - } - } - } - - func progress(fromTime: Time, _ toTime: Time, _ currentTime: Time) -> Progress { - let p = (currentTime-fromTime)/(toTime-fromTime) - return min(1, max(0, p)) - } - - func setTime(time: Time) { - self.object.setValue(T.objectify(self[time]!), forKeyPath: self.keyPath) - } -} - -public func ==(lhs: Interpolation, rhs: Interpolation) -> Bool { - return lhs.object == rhs.object && lhs.keyPath == rhs.keyPath -} - -extension Array where Element: Comparable { - func elementPairs() -> [(Element, Element)]? { - if self.count >= 2 { - var elementPairs: [(Element, Element)] = [] - - for (i, e) in self.sort().enumerate() { - if i+1 < self.count { - elementPairs.append((e, self[i+1])) - } - } - - return elementPairs - } - - return nil - } - - func elementsAround(element: Element) -> (Element?, Element?)? { - if let pairs = self.elementPairs() { - - let minElement = pairs.first!.0 - let maxElement = pairs.last!.1 - - if element < minElement { - return (nil, minElement) - } - - if element > maxElement { - return (maxElement, nil) - } - - for (e1, e2) in pairs where (e1...e2).contains(element) { - return (e1, e2) - } - } - - return nil - } -} \ No newline at end of file diff --git a/Popsicle/Interpolator.swift b/Popsicle/Interpolator.swift deleted file mode 100755 index 23c2e00..0000000 --- a/Popsicle/Interpolator.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// Interpolator.swift -// Popsicle -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -/// Value type on which interpolation values rely on. -public typealias Time = Double - -/// `Interpolator` collects and coordinates a set of related interpolations through its `time` property. -public class Interpolator { - var interpolations = [Timeable]() - - public init() {} - - public func addInterpolation(interpolation: Interpolation) { - self.interpolations.append(interpolation) - } - - public var time: Time = 0 { - didSet { - self.interpolations.forEach { $0.setTime(self.time) } - } - } - - public func removeInterpolation(interpolation: Interpolation) { - for (index, element) in self.interpolations.enumerate() { - if let interpolation = element as? Interpolation { - if interpolation == interpolation { - self.interpolations.removeAtIndex(index) - } - } - } - } - - /// Removes all interpolations containing the specified object. - public func removeInterpolations(forObject object: NSObject) { - for (index, element) in self.interpolations.enumerate() { - if let interpolation = element as? ObjectReferable { - if interpolation.objectReference == object { - self.interpolations.removeAtIndex(index) - self.removeInterpolations(forObject: object) // Recursivity FTW - return - } - } - } - } - - public func removeAllInterpolations() { - self.interpolations.removeAll() - } -} \ No newline at end of file diff --git a/Popsicle/KeyPath.swift b/Popsicle/KeyPath.swift deleted file mode 100755 index 0cbb5b4..0000000 --- a/Popsicle/KeyPath.swift +++ /dev/null @@ -1,157 +0,0 @@ -// -// KeyPath.swift -// Popsicle -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -/// `KeyPathable` defines how a value is transformed to a valid `NSObject` key path. -public protocol KeyPathable { - func stringify() -> String -} - -extension String : KeyPathable { - public func stringify() -> String { - return self - } -} - -extension NSLayoutAttribute: KeyPathable { - public func stringify() -> String { - - var type = "UnknownAttribute" - - switch(self) { - case .Left: - type = "Left" - - case .Right: - type = "Right" - - case .Top: - type = "Top" - - case .Bottom: - type = "Bottom" - - case .Leading: - type = "Leading" - - case .Trailing: - type = "Trailing" - - case .Width: - type = "Width" - - case .Height: - type = "Height" - - case .CenterX: - type = "CenterX" - - case .CenterY: - type = "CenterY" - - case .LastBaseline: - type = "Baseline" - - case .FirstBaseline: - type = "FirstBaseline" - - case .LeftMargin: - type = "LeftMargin" - - case .RightMargin: - type = "RightMargin" - - case .TopMargin: - type = "TopMargin" - - case .BottomMargin: - type = "BottomMargin" - - case .LeadingMargin: - type = "LeadingMargin" - - case .TrailingMargin: - type = "TrailingMargin" - - case .CenterXWithinMargins: - type = "CenterXWithinMargins" - - case .CenterYWithinMargins: - type = "CenterYWithinMargins" - - case .NotAnAttribute: - type = "NotAnAttribute" - } - - return "NSLayoutAttribute." + type - } -} - -/// `KeyPath` defines a `NSObject`'s key path, constrained to specific `NSObject` and `Interpolable` types for higher safety. -public struct KeyPath { - let keyPathable: KeyPathable - - public init(keyPathable: KeyPathable) { - self.keyPathable = keyPathable - } -} - -public let alpha = KeyPath(keyPathable: "alpha") -public let backgroundColor = KeyPath(keyPathable: "backgroundColor") -public let barTintColor = KeyPath(keyPathable: "barTintColor") -public let borderColor = KeyPath(keyPathable: "borderColor") -public let borderWidth = KeyPath(keyPathable: "borderWidth") -public let constant = KeyPath(keyPathable: "constant") -public let cornerRadius = KeyPath(keyPathable: "cornerRadius") -public let hidden = KeyPath(keyPathable: "hidden") -public let textColor = KeyPath(keyPathable: "textColor") -public let tintColor = KeyPath(keyPathable: "tintColor") -public let transform = KeyPath(keyPathable: "transform") - -public let baselineConstraint = KeyPath(keyPathable: NSLayoutAttribute.LastBaseline) -public let firstBaselineConstraint = KeyPath(keyPathable: NSLayoutAttribute.FirstBaseline) - -public let topConstraint = KeyPath(keyPathable: NSLayoutAttribute.Top) -public let leftConstraint = KeyPath(keyPathable: NSLayoutAttribute.Left) -public let rightConstraint = KeyPath(keyPathable: NSLayoutAttribute.Right) -public let bottomConstraint = KeyPath(keyPathable: NSLayoutAttribute.Bottom) -public let leadingConstraint = KeyPath(keyPathable: NSLayoutAttribute.Leading) -public let trailingConstraint = KeyPath(keyPathable: NSLayoutAttribute.Trailing) - -public let leftMarginConstraint = KeyPath(keyPathable: NSLayoutAttribute.LeftMargin) -public let rightMarginConstraint = KeyPath(keyPathable: NSLayoutAttribute.RightMargin) -public let topMarginConstraint = KeyPath(keyPathable: NSLayoutAttribute.TopMargin) -public let bottomMarginConstraint = KeyPath(keyPathable: NSLayoutAttribute.BottomMargin) -public let leadingMarginConstraint = KeyPath(keyPathable: NSLayoutAttribute.LeadingMargin) -public let trailingMarginConstraint = KeyPath(keyPathable: NSLayoutAttribute.TrailingMargin) - -public let centerXConstraint = KeyPath(keyPathable: NSLayoutAttribute.CenterX) -public let centerYConstraint = KeyPath(keyPathable: NSLayoutAttribute.CenterY) - -public let centerXWithinMarginsConstraint = KeyPath(keyPathable: NSLayoutAttribute.CenterXWithinMargins) -public let centerYWithinMarginsConstraint = KeyPath(keyPathable: NSLayoutAttribute.CenterYWithinMargins) - -public let widthConstraint = KeyPath(keyPathable: NSLayoutAttribute.Width) -public let heightConstraint = KeyPath(keyPathable: NSLayoutAttribute.Height) - -extension NSObject { - static func filteredObjectAndKeyPath(withObject object: T, andKeyPath keyPath: KeyPath) -> (NSObject, String) { - if let view = object as? UIView, let superview = view.superview, let attribute = keyPath.keyPathable as? NSLayoutAttribute { - - let constrainedView = (attribute == .Width || attribute == .Height) ? view : superview - - for constraint in constrainedView.constraints where - !constraint.isKindOfClass(NSClassFromString("NSContentSizeLayoutConstraint")!) && - ((constraint.firstItem as? NSObject == view && constraint.firstAttribute == attribute) || - (constraint.secondItem as? NSObject == view && constraint.secondAttribute == attribute)) { - return (constraint, constant.keyPathable.stringify()) - } - } - - return (object, keyPath.keyPathable.stringify()) - } -} diff --git a/Popsicle/Popsicle.h b/Popsicle/Popsicle.h deleted file mode 100755 index 6627d4a..0000000 --- a/Popsicle/Popsicle.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Popsicle.h -// Popsicle -// -// Created by David Román Aguirre on 01/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -@import UIKit; - -//! Project version number for Popsicle. -FOUNDATION_EXPORT double PopsicleVersionNumber; - -//! Project version string for Popsicle. -FOUNDATION_EXPORT const unsigned char PopsicleVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import diff --git a/PopsicleDemo/AppDelegate.swift b/PopsicleDemo/AppDelegate.swift deleted file mode 100755 index c0b9385..0000000 --- a/PopsicleDemo/AppDelegate.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// AppDelegate.swift -// PopsicleDemo -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -import UIKit - -@UIApplicationMain - -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - - self.window = UIWindow(frame: UIScreen.mainScreen().bounds) - self.window?.rootViewController = UINavigationController(rootViewController: ViewController()) - self.window?.makeKeyAndVisible() - - return true - } -} - diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100755 index 97874f7..0000000 --- a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "images" : [ - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-40@2x-1.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-60@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-29.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-29@2x-1.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-40.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-76.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-83.5@2x.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29.png deleted file mode 100644 index 0450b19..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png deleted file mode 100644 index 7363a9d..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png deleted file mode 100644 index 7363a9d..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png deleted file mode 100644 index a889ee3..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40.png deleted file mode 100644 index 8d11f6e..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png deleted file mode 100644 index da37d23..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png deleted file mode 100644 index da37d23..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png deleted file mode 100644 index 1c49000..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png deleted file mode 100644 index d19fb25..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png deleted file mode 100644 index 9cdc0d8..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-76.png deleted file mode 100644 index c3af9cb..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-76.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png deleted file mode 100644 index 70bb530..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png deleted file mode 100644 index 0f77b14..0000000 Binary files a/PopsicleDemo/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png and /dev/null differ diff --git a/PopsicleDemo/Assets.xcassets/Contents.json b/PopsicleDemo/Assets.xcassets/Contents.json deleted file mode 100755 index da4a164..0000000 --- a/PopsicleDemo/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/PopsicleDemo/Assets.xcassets/logo.imageset/Contents.json b/PopsicleDemo/Assets.xcassets/logo.imageset/Contents.json deleted file mode 100755 index c369d6f..0000000 --- a/PopsicleDemo/Assets.xcassets/logo.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "logo.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/PopsicleDemo/Base.lproj/LaunchScreen.storyboard b/PopsicleDemo/Base.lproj/LaunchScreen.storyboard deleted file mode 100755 index 78686cd..0000000 --- a/PopsicleDemo/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PopsicleDemo/Info.plist b/PopsicleDemo/Info.plist deleted file mode 100755 index 5f4192f..0000000 --- a/PopsicleDemo/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Popsicle - CFBundlePackageType - APPL - CFBundleShortVersionString - 2.0.1 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/PopsicleDemo/PageScrollView.swift b/PopsicleDemo/PageScrollView.swift deleted file mode 100755 index a364143..0000000 --- a/PopsicleDemo/PageScrollView.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// PageScrollView.swift -// Popsicle -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -import UIKit - -class FirstPageView: UIView { - @IBOutlet weak var label: UILabel? - @IBOutlet weak var imageView: UIImageView? -} - -class SecondPageView: UIView { - @IBOutlet weak var label: UILabel? -} - -class ThirdPageView: UIView { - @IBOutlet weak var label1: UILabel? - @IBOutlet weak var label2: UILabel? -} - -class FourthPageView: UIView { - @IBOutlet weak var label: UILabel? -} - -class PageScrollView: DRPageScrollView { - let firstPageView: FirstPageView - let secondPageView: SecondPageView - let thirdPageView: ThirdPageView - let fourthPageView: FourthPageView - - override init(frame: CGRect) { - let views = UIView.viewsByClassInNibNamed("PageViews") - self.firstPageView = views["PopsicleDemo.FirstPageView"] as! FirstPageView - self.secondPageView = views["PopsicleDemo.SecondPageView"] as! SecondPageView - self.thirdPageView = views["PopsicleDemo.ThirdPageView"] as! ThirdPageView - self.fourthPageView = views["PopsicleDemo.FourthPageView"] as! FourthPageView - super.init(frame: frame) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func didMoveToSuperview() { - if self.superview != nil { - for pv in [firstPageView, secondPageView, thirdPageView, fourthPageView] { - self.addPageWithHandler { pageView in - pageView.addSubview(pv) - pv.pinToSuperviewEdges() - } - } - } - } -} diff --git a/PopsicleDemo/PageViews.xib b/PopsicleDemo/PageViews.xib deleted file mode 100755 index db6abd3..0000000 --- a/PopsicleDemo/PageViews.xib +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PopsicleDemo/PopsicleDemo-Bridging-Header.h b/PopsicleDemo/PopsicleDemo-Bridging-Header.h deleted file mode 100755 index 99deb86..0000000 --- a/PopsicleDemo/PopsicleDemo-Bridging-Header.h +++ /dev/null @@ -1,5 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -#import "DRPageScrollView.h" \ No newline at end of file diff --git a/PopsicleDemo/UIView+Utils.swift b/PopsicleDemo/UIView+Utils.swift deleted file mode 100755 index 3604871..0000000 --- a/PopsicleDemo/UIView+Utils.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// UIView+Utils.swift -// Popsicle -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -extension UIView { - static func viewsByClassInNibNamed(name: String) -> [String: UIView] { - var viewsByClass = [String: UIView]() - - let nibViews = NSBundle.mainBundle().loadNibNamed(name, owner: self, options: nil) - - for view in nibViews { - if let v = view as? UIView { - viewsByClass[NSStringFromClass(v.dynamicType)] = v - } - } - - return viewsByClass - } - - func pinToSuperviewEdges() { - self.translatesAutoresizingMaskIntoConstraints = false - - for attribute in [.Top, .Left, .Bottom, .Right] as [NSLayoutAttribute] { - let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: .Equal, toItem: self.superview, attribute: attribute, multiplier: 1, constant: 0) - - self.superview?.addConstraint(constraint) - } - } -} \ No newline at end of file diff --git a/PopsicleDemo/ViewController.swift b/PopsicleDemo/ViewController.swift deleted file mode 100755 index 6144d70..0000000 --- a/PopsicleDemo/ViewController.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// ViewController.swift -// PopsicleDemo -// -// Created by David Román Aguirre on 04/11/15. -// Copyright © 2015 David Román Aguirre. All rights reserved. -// - -import UIKit -import Popsicle - -class ViewController: UIViewController, UIScrollViewDelegate { - - let pageScrollView: PageScrollView - let interpolator: Interpolator - - init() { - self.pageScrollView = PageScrollView(frame: CGRectZero) - self.interpolator = Interpolator() - super.init(nibName: nil, bundle: nil) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - self.title = "Popsicle" - self.view.backgroundColor = UIColor.whiteColor() - - self.pageScrollView.delegate = self - self.view.addSubview(self.pageScrollView) - self.pageScrollView.pinToSuperviewEdges() - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - - self.interpolator.removeAllInterpolations() - - let backgroundColorInterpolation = Interpolation(self.view, backgroundColor) - backgroundColorInterpolation[1, 3] = UIColor.whiteColor() - backgroundColorInterpolation[1.7, 2] = UIColor(red: 254/255, green: 134/255, blue: 44/255, alpha: 1) - self.interpolator.addInterpolation(backgroundColorInterpolation) - - let barTintColorInterpolation = Interpolation(self.navigationController!.navigationBar, barTintColor) - barTintColorInterpolation[1, 3] = UIColor.whiteColor() - barTintColorInterpolation[1.7, 2] = UIColor(red: 244255, green: 219/255, blue: 165/255, alpha: 1) - self.interpolator.addInterpolation(barTintColorInterpolation) - - if let imageView = self.pageScrollView.firstPageView.imageView { - let xInterpolation = Interpolation(imageView, centerXConstraint) - xInterpolation[0] = 0 - xInterpolation.setEasingFunction(EasingFunctionEaseInQuad, forTime: 0) - xInterpolation[1] = -self.pageScrollView.frame.width - xInterpolation[2] = -self.pageScrollView.frame.width*2 - xInterpolation[3] = -self.pageScrollView.frame.width*3 - self.interpolator.addInterpolation(xInterpolation) - - let yInterpolation = Interpolation(imageView, centerYConstraint) - yInterpolation[0] = 0 - yInterpolation[1, 2] = -self.pageScrollView.frame.height/2+80 - yInterpolation[3] = 0 - self.interpolator.addInterpolation(yInterpolation) - - let alphaInterpolation = Interpolation(imageView, alpha) - alphaInterpolation[1] = 1 - alphaInterpolation[2] = 0 - alphaInterpolation[3] = 0.25 - self.interpolator.addInterpolation(alphaInterpolation) - - let transformInterpolation = Interpolation(imageView, transform) - transformInterpolation[0, 1, 2] = CGAffineTransformIdentity - transformInterpolation[0.25] = CGAffineTransformMake(0, 0, 1.1, 1.1, 0) - transformInterpolation[3] = CGAffineTransformMake(0, 0, 1.4, 1.4, 60) - self.interpolator.addInterpolation(transformInterpolation) - } - - if let label1 = self.pageScrollView.firstPageView.label { - let alphaInterpolation = Interpolation(label1, alpha) - alphaInterpolation[0] = 1 - alphaInterpolation[0.4] = 0 - self.interpolator.addInterpolation(alphaInterpolation) - } - - if let label2 = self.pageScrollView.secondPageView.label { - let scaleInterpolation = Interpolation(label2, transform) - scaleInterpolation[0] = CGAffineTransformMake(0, 0, 0.6, 0.6, 0) - scaleInterpolation[1] = CGAffineTransformMake(0, 0, 1, 1, 0) - self.interpolator.addInterpolation(scaleInterpolation) - - let alphaInterpolation = Interpolation(label2, alpha) - alphaInterpolation[1] = 1 - alphaInterpolation[1.7] = 0 - self.interpolator.addInterpolation(alphaInterpolation) - } - - if let label3 = self.pageScrollView.thirdPageView.label1, let label4 = self.pageScrollView.thirdPageView.label2 { - let translateInterpolation1 = Interpolation(label3, transform) - translateInterpolation1[1] = CGAffineTransformMake(100, 0, 1, 1, 0) - translateInterpolation1[2] = CGAffineTransformIdentity - translateInterpolation1[3] = CGAffineTransformMake(-100, 0, 1, 1, 0) - self.interpolator.addInterpolation(translateInterpolation1) - - let translateInterpolation2 = Interpolation(label4, transform) - translateInterpolation2[1] = CGAffineTransformMake(300, 0, 1, 1, 0) - translateInterpolation2[2] = CGAffineTransformIdentity - translateInterpolation2[3] = CGAffineTransformMake(-300, 0, 1, 1, 0) - self.interpolator.addInterpolation(translateInterpolation2) - } - } - - func scrollViewDidScroll(scrollView: UIScrollView) { - self.interpolator.time = Double(scrollView.contentOffset.x/scrollView.frame.size.width) - } -} \ No newline at end of file diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 3bcaad3..276dc02 --- a/README.md +++ b/README.md @@ -1,98 +1,3 @@ -> **THIS PROJECT IS NO LONGER MAINTAINED.** +# Popsicle ---- - -

- Popsicle header -

- -

- Carthage compatible - CocoaPods compatible -

- -

- GIF 1 -

- -Popsicle is a Swift framework for creating and managing interpolations of different value types with built-in UIKit support. - -## Installation - -#### Carthage - -``` -github "DavdRoman/Popsicle" -``` - -#### CocoaPods - -```ruby -pod 'Popsicle' -``` - -#### Manual - -Drag and copy all files in the [__Popsicle__](Popsicle) folder into your project. - -## At a glance - -#### Interpolating UIView (or any other NSObject) values - -First, you need an `Interpolator` instance: - -```swift -let interpolator = Interpolator() -``` - -Next, you need to add some `Interpolation` instances to your interpolator. In the example below, we are going to interpolate the alpha value of a UIView for times between 0 and 150: - -```swift -let interpolation = Interpolation(yourView, alpha) -interpolation[0] = 0 -interpolation[150] = 1 -self.interpolator.addInterpolation(interpolation) -``` - -Note `alpha` is a built-in `KeyPath` constant. Popsicle offers a nice set of [__UIKit-related KeyPaths__](Popsicle/KeyPath.swift) ready to be used. You may also use a completely custom key path. - -You can also modify the easing function used at a given time: - -```swift -interpolation.setEasingFunction(EasingFunctionEaseOutQuad, forTime: 0) -``` - -There's a bunch of [__built-in easing functions__](Popsicle/EasingFunction.swift) to choose from. - -Finally, just make your `interpolator` vary its `time` depending on whatever you want. For example, the content offset of a `UITableView`: - -```swift -func scrollViewDidScroll(scrollView: UIScrollView) { - interpolator.time = Double(scrollView.contentOffset.y) -} -``` - -#### Interpolating custom values - -You can declare a value type as interpolable by making it conform to the `Interpolable` protocol. - -As an example, check out how `CGPoint` conforms to `Interpolable`: - -```swift -extension CGSize: Interpolable { - public static func interpolate(from fromValue: CGSize, to toValue: CGSize, withProgress progress: Progress) -> CGSize { - let width = CGFloat.interpolate(from: fromValue.width, to: toValue.width, withProgress: progress) - let height = CGFloat.interpolate(from: fromValue.height, to: toValue.height, withProgress: progress) - - return CGSizeMake(width, height) - } - - public static func objectify(value: CGSize) -> AnyObject { - return NSValue(CGSize: value) - } -} -``` - -## License - -Popsicle is available under the MIT license. +A description of this package. diff --git a/Sources/Popsicle/Interpolator.swift b/Sources/Popsicle/Interpolator.swift new file mode 100644 index 0000000..c27ef76 --- /dev/null +++ b/Sources/Popsicle/Interpolator.swift @@ -0,0 +1,42 @@ +import UIKit + +public final class Interpolator { + + var keyframes: Timeline<[Keyframe]> = [:] + + public init() {} + + public func addKeyframe( + _ time: Time, + _ curve: UIView.AnimationCurve = .linear, + _ content: @escaping () -> Void + ) { + addKeyframe(time, UICubicTimingParameters(animationCurve: curve), content) + } + + public func addKeyframe( + _ time: Time, + _ curve: UITimingCurveProvider, + _ content: @escaping () -> Void + ) { + let keyframe = Keyframe(curve: curve, content: content) + keyframes[time, default: []].append(keyframe) + } + + public func callAsFunction(_ time: Time) { + guard + keyframes.count >= 2, + let (currentKeytime, currentKeyframe) = keyframes.current(for: time) + else { + return + } + + keyframes.allPrevious(before: currentKeytime).lazy.flatMap(\.1)() + currentKeyframe() + + if let (nextKeytime, nextKeyframe) = keyframes.next(after: time) { + let relativeTime = (time - currentKeytime) / (nextKeytime - currentKeytime) + nextKeyframe(relativeTime) + } + } +} diff --git a/Sources/Popsicle/Keyframe.swift b/Sources/Popsicle/Keyframe.swift new file mode 100644 index 0000000..0b81f5c --- /dev/null +++ b/Sources/Popsicle/Keyframe.swift @@ -0,0 +1,34 @@ +import UIKit + +struct Keyframe { + var curve: UITimingCurveProvider + var content: () -> Void + + init(curve: UITimingCurveProvider, content: @escaping () -> Void) { + self.curve = curve + self.content = content + } + + func callAsFunction(_ time: Time? = nil) { + if let time = time { + var animator: UIViewPropertyAnimator! = .init(duration: .zero, timingParameters: curve) + animator.addAnimations(content) + animator.scrubsLinearly = false + animator.fractionComplete = time + DispatchQueue.main.async { + DispatchQueue.main.async { + animator.stopAnimation(true) + animator = nil + } + } + } else { + content() + } + } +} + +extension Collection where Element == Keyframe { + func callAsFunction(_ time: Time? = nil) { + forEach { $0(time) } + } +} diff --git a/Sources/Popsicle/Timeline.swift b/Sources/Popsicle/Timeline.swift new file mode 100644 index 0000000..27cfa3e --- /dev/null +++ b/Sources/Popsicle/Timeline.swift @@ -0,0 +1,43 @@ +import SortedCollections + +public typealias Time = Double + +typealias Timeline = SortedDictionary + +extension Timeline where Key == Time { + var initialTime: Time? { + keys.first + } + + var finalTime: Time? { + keys.last + } + + func current(for time: Time) -> (Time, Value)? { + if let value = self[time] { + return (time, value) + } + + if let time = keys.lazy.reversed().first(where: { time >= $0 }), let value = self[time] { + return (time, value) + } + + return first + } + + func next(after time: Time) -> (Time, Value)? { + if let initialTime = initialTime, time < initialTime { + return nil + } + + if let time = keys.first(where: { $0 > time }), let value = self[time] { + return (time, value) + } + + return nil + } + + func allPrevious(before time: Time) -> [(Time, Value)] { + filter { $0.key < time } + } +} diff --git a/Tests/PopsicleTests/InterpolatorTests.swift b/Tests/PopsicleTests/InterpolatorTests.swift new file mode 100644 index 0000000..2206001 --- /dev/null +++ b/Tests/PopsicleTests/InterpolatorTests.swift @@ -0,0 +1,127 @@ +@testable import Popsicle +import XCTest + +final class InterpolatorTests: XCTestCase { + func test() throws { + let view = UIView() + UIWindow().addSubview(view) // makes presentation layers behave as expected + + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 0) + XCTAssertEqual($0.opacity, 1, accuracy: 0.0001) + } + + let sut = Interpolator() + sut.addKeyframe(0) { + view.frame.origin.x = 100 + view.alpha = 0 + } + sut.addKeyframe(100) { + view.frame.origin.x = 200 + view.alpha = 0.5 + } + sut.addKeyframe(200) { + view.frame.origin.x = 300 + // no alpha + } + sut.addKeyframe(400) { + view.frame.origin.x = 1000 + view.alpha = 1 + } + + let assertions: [() throws -> Void] = [ + { + sut(-100) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 100, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 0, accuracy: 0.0001) + } + }, + { + sut(0) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 100, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 0, accuracy: 0.0001) + } + }, + { + sut(50) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 150, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 0.25, accuracy: 0.0001) + } + }, + { + sut(100) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 200, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 0.5, accuracy: 0.0001) + } + }, + { + sut(150) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 250, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 0.5, accuracy: 0.0001) + } + }, + { + sut(300) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 650, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 0.75, accuracy: 0.0001) + } + }, + { + sut(400) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 1000, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 1, accuracy: 0.0001) + } + }, + { + sut(1200) + try view.assertOnPresentationLayer { + XCTAssertEqual($0.frame.origin.x, 1000, accuracy: 0.0001) + XCTAssertEqual($0.opacity, 1, accuracy: 0.0001) + } + }, + ] + + var rng = WyRand() + try Array(repeating: assertions, count: 5).flatMap { $0 }.shuffled(using: &rng)() + } +} + +extension UIView { + func assertOnPresentationLayer( + _ assertions: (CALayer) -> Void, + file: StaticString = #file, + line: UInt = #line + ) throws -> Void { + RunLoop.main.run(until: Date() + 0.1) + let layer = try XCTUnwrap(self.layer.presentation(), file: file, line: line) + assertions(layer) + } +} + +struct WyRand: RandomNumberGenerator { + private var state: UInt64 + + init(seed: UInt64 = .random(in: 0...100000)) { + state = seed + } + + mutating func next() -> UInt64 { + state &+= 0xa0761d6478bd642f + let mul = state.multipliedFullWidth(by: state ^ 0xe7037ed1a0b428db) + return mul.high ^ mul.low + } +} + + +extension Collection where Element == () throws -> Void { + func callAsFunction() throws { + try forEach { try $0() } + } +} diff --git a/Tests/PopsicleTests/TimelineTests.swift b/Tests/PopsicleTests/TimelineTests.swift new file mode 100644 index 0000000..5a2219f --- /dev/null +++ b/Tests/PopsicleTests/TimelineTests.swift @@ -0,0 +1,92 @@ +@testable import Popsicle +import XCTest + +final class TimelineTests: XCTestCase { + let sut: Timeline = [ + 0: "a", + 250: "b", + 500: "c", + 750: "d", + 1000: "e", + ] + + func testCurrent() { + XCTAssert(sut.current(for: -.greatestFiniteMagnitude)! == (0, "a")) + XCTAssert(sut.current(for: -1)! == (0, "a")) + XCTAssert(sut.current(for: 0)! == (0, "a")) + XCTAssert(sut.current(for: 1)! == (0, "a")) + + XCTAssert(sut.current(for: 249)! == (0, "a")) + XCTAssert(sut.current(for: 250)! == (250, "b")) + XCTAssert(sut.current(for: 251)! == (250, "b")) + + XCTAssert(sut.current(for: 499)! == (250, "b")) + XCTAssert(sut.current(for: 500)! == (500, "c")) + XCTAssert(sut.current(for: 501)! == (500, "c")) + + XCTAssert(sut.current(for: 749)! == (500, "c")) + XCTAssert(sut.current(for: 750)! == (750, "d")) + XCTAssert(sut.current(for: 751)! == (750, "d")) + + XCTAssert(sut.current(for: 999)! == (750, "d")) + XCTAssert(sut.current(for: 1000)! == (1000, "e")) + XCTAssert(sut.current(for: 1001)! == (1000, "e")) + XCTAssert(sut.current(for: .greatestFiniteMagnitude)! == (1000, "e")) + } + + func testNext() { + XCTAssert(sut.next(after: -.greatestFiniteMagnitude) == nil) + XCTAssert(sut.next(after: -1) == nil) + XCTAssert(sut.next(after: 0)! == (250, "b")) + XCTAssert(sut.next(after: 1)! == (250, "b")) + + XCTAssert(sut.next(after: 249)! == (250, "b")) + XCTAssert(sut.next(after: 250)! == (500, "c")) + XCTAssert(sut.next(after: 251)! == (500, "c")) + + XCTAssert(sut.next(after: 499)! == (500, "c")) + XCTAssert(sut.next(after: 500)! == (750, "d")) + XCTAssert(sut.next(after: 501)! == (750, "d")) + + XCTAssert(sut.next(after: 749)! == (750, "d")) + XCTAssert(sut.next(after: 750)! == (1000, "e")) + XCTAssert(sut.next(after: 751)! == (1000, "e")) + + XCTAssert(sut.next(after: 999)! == (1000, "e")) + XCTAssert(sut.next(after: 1000) == nil) + XCTAssert(sut.next(after: 1001) == nil) + XCTAssert(sut.next(after: .greatestFiniteMagnitude) == nil) + } + + func testAllPrevious() { + XCTAssert(sut.allPrevious(before: -.greatestFiniteMagnitude) == []) + XCTAssert(sut.allPrevious(before: -1) == []) + XCTAssert(sut.allPrevious(before: 0) == []) + XCTAssert(sut.allPrevious(before: 1) == [(0, "a")]) + + XCTAssert(sut.allPrevious(before: 249) == [(0, "a")]) + XCTAssert(sut.allPrevious(before: 250) == [(0, "a")]) + XCTAssert(sut.allPrevious(before: 251) == [(0, "a"), (250, "b")]) + + XCTAssert(sut.allPrevious(before: 499) == [(0, "a"), (250, "b")]) + XCTAssert(sut.allPrevious(before: 500) == [(0, "a"), (250, "b")]) + XCTAssert(sut.allPrevious(before: 501) == [(0, "a"), (250, "b"), (500, "c")]) + + XCTAssert(sut.allPrevious(before: 749) == [(0, "a"), (250, "b"), (500, "c")]) + XCTAssert(sut.allPrevious(before: 750) == [(0, "a"), (250, "b"), (500, "c")]) + XCTAssert(sut.allPrevious(before: 751) == [(0, "a"), (250, "b"), (500, "c"), (750, "d")]) + + XCTAssert(sut.allPrevious(before: 999) == [(0, "a"), (250, "b"), (500, "c"), (750, "d")]) + XCTAssert(sut.allPrevious(before: 1000) == [(0, "a"), (250, "b"), (500, "c"), (750, "d")]) + XCTAssert(sut.allPrevious(before: 1001) == [(0, "a"), (250, "b"), (500, "c"), (750, "d"), (1000, "e")]) + XCTAssert(sut.allPrevious(before: .greatestFiniteMagnitude) == [(0, "a"), (250, "b"), (500, "c"), (750, "d"), (1000, "e")]) + } +} + +// TODO: remove when tuples conform to Equatable +// https://github.com/apple/swift-evolution/blob/main/proposals/0283-tuples-are-equatable-comparable-hashable.md +extension Array where Element == (Time, Character) { + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.elementsEqual(rhs, by: { $0 == $1 }) + } +}