├── .gitignore ├── .swiftlint.yml ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── ImageTransition.podspec ├── ImageTransition.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── ImageTransition.xcscheme │ └── Sample.xcscheme ├── ImageTransition ├── ImageTransition.h ├── ImageTransitionDelegate.swift ├── ImageTransitionable.swift ├── ImageTransitioning.swift ├── Info.plist ├── UIImage+.swift ├── UIImageView+.swift └── UIView+.swift ├── ImageTransitionTests ├── ImageTransitionAnimatorTests.swift ├── ImageTransitionDelegateTests.swift ├── Info.plist ├── UIImage+Tests.swift ├── UIImageView+Tests.swift └── UIView+Tests.swift ├── LICENSE ├── Mintfile ├── Package.swift ├── README.md ├── Sample ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── apple.imageset │ │ ├── Contents.json │ │ └── apple-food-fruit-39803.jpg │ ├── kiwi.imageset │ │ ├── Contents.json │ │ └── food-fresh-fruit-51312.jpg │ ├── lemon.imageset │ │ ├── Contents.json │ │ └── acid-bright-citrus-1414110.jpg │ ├── orange.imageset │ │ ├── Contents.json │ │ └── citrus-food-fresh-42059.jpg │ ├── strawberry.imageset │ │ ├── Contents.json │ │ └── close-up-food-fruit-59945.jpg │ └── tomato.imageset │ │ ├── Contents.json │ │ └── cherry-tomatoes-food-freshness-48802.jpg ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist ├── ItemCell.swift ├── ItemCell.xib ├── ItemDetailViewController.swift ├── ItemDetailViewController.xib ├── ItemListViewController.swift └── ItemListViewController.xib └── docs └── assets ├── sample_01.gif ├── sample_01.mov ├── sample_02.gif └── sample_02.mov /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/90cb995068518f26e3c1305401ffa9e4ae8b110b/Swift.gitignore 2 | 3 | # Xcode 4 | # 5 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData/ 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata/ 21 | 22 | ## Other 23 | *.moved-aside 24 | *.xccheckout 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | .build/ 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | # 53 | # Add this line if you want to avoid checking in source code from the Xcode workspace 54 | # *.xcworkspace 55 | 56 | # Carthage 57 | # 58 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 59 | Carthage/Checkouts 60 | Carthage/Build 61 | 62 | # fastlane 63 | # 64 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 65 | # screenshots whenever they are needed. 66 | # For more information about the recommended setup visit: 67 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 68 | 69 | fastlane/report.xml 70 | # fastlane/Preview.html 71 | # fastlane/screenshots/**/*.png 72 | fastlane/test_output 73 | 74 | # Code Injection 75 | # 76 | # After new code Injection tools there's a generated folder /iOSInjectionProject 77 | # https://github.com/johnno1962/injectionforxcode 78 | 79 | iOSInjectionProject/ 80 | 81 | 82 | ### https://raw.github.com/github/gitignore/90cb995068518f26e3c1305401ffa9e4ae8b110b/Global/Xcode.gitignore 83 | 84 | # Xcode 85 | # 86 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 87 | 88 | ## User settings 89 | xcuserdata/ 90 | 91 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 92 | *.xcscmblueprint 93 | *.xccheckout 94 | 95 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 96 | build/ 97 | DerivedData/ 98 | *.moved-aside 99 | *.pbxuser 100 | !default.pbxuser 101 | *.mode1v3 102 | !default.mode1v3 103 | *.mode2v3 104 | !default.mode2v3 105 | *.perspectivev3 106 | !default.perspectivev3 -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - attributes 3 | - array_init 4 | - closure_body_length 5 | - conditional_returns_on_newline 6 | - cyclomatic_complexity 7 | - discouraged_optional_boolean 8 | - discouraged_optional_collection 9 | - explicit_acl 10 | - explicit_enum_raw_value 11 | - explicit_top_level_acl 12 | - explicit_type_interface 13 | - extension_access_modifier 14 | - fatal_error_message 15 | - file_header 16 | - file_types_order 17 | - file_length 18 | - function_body_length 19 | - function_default_parameter_at_end 20 | - function_parameter_count 21 | - indentation_width 22 | - implicit_return 23 | - lower_acl_than_parent 24 | - no_extension_access_modifier 25 | - no_grouping_extension 26 | - number_separator 27 | - missing_docs 28 | - modifier_order 29 | - multiline_arguments 30 | - multiple_closures_with_trailing_closure 31 | - multiline_arguments_brackets 32 | - multiline_parameters_brackets 33 | - object_literal 34 | - operator_usage_whitespace 35 | - pattern_matching_keywords 36 | - private_action 37 | - private_outlet 38 | - prefixed_toplevel_constant 39 | - prohibited_interface_builder 40 | - raw_value_for_camel_cased_codable_enum 41 | - required_deinit 42 | - sorted_imports 43 | - strict_fileprivate 44 | - strong_iboutlet 45 | - switch_case_on_newline 46 | - trailing_whitespace 47 | - trailing_closure 48 | - type_contents_order 49 | - todo 50 | - unavailable_function 51 | - unneeded_parentheses_in_closure_argument 52 | - unused_setter_value 53 | - xctfail_message 54 | - xct_specific_matcher 55 | - line_length 56 | - force_unwrapping 57 | - let_var_whitespace 58 | - file_name 59 | 60 | opt_in_rules: 61 | - anyobject_protocol 62 | - array_init 63 | - attributes 64 | - closure_body_length 65 | - closure_end_indentation 66 | - closure_spacing 67 | - collection_alignment 68 | - conditional_returns_on_newline 69 | - contains_over_filter_count 70 | - contains_over_filter_is_empty 71 | - contains_over_first_not_nil 72 | - contains_over_range_nil_comparison 73 | - convenience_type 74 | - discouraged_object_literal 75 | - discouraged_optional_boolean 76 | - discouraged_optional_collection 77 | - empty_collection_literal 78 | - empty_count 79 | - empty_string 80 | - empty_xctest_method 81 | - enum_case_associated_values_count 82 | - expiring_todo 83 | - explicit_acl 84 | - explicit_enum_raw_value 85 | - explicit_init 86 | - explicit_self 87 | - explicit_top_level_acl 88 | - explicit_type_interface 89 | - extension_access_modifier 90 | - fallthrough 91 | - fatal_error_message 92 | - file_header 93 | - file_name 94 | - file_name_no_space 95 | - file_types_order 96 | - first_where 97 | - flatmap_over_map_reduce 98 | - force_unwrapping 99 | - function_default_parameter_at_end 100 | - identical_operands 101 | - implicit_return 102 | - implicitly_unwrapped_optional 103 | - indentation_width 104 | - joined_default_parameter 105 | - last_where 106 | - legacy_multiple 107 | - legacy_random 108 | - let_var_whitespace 109 | - literal_expression_end_indentation 110 | - lower_acl_than_parent 111 | - missing_docs 112 | - modifier_order 113 | - multiline_arguments 114 | - multiline_arguments_brackets 115 | - multiline_function_chains 116 | - multiline_literal_brackets 117 | - multiline_parameters 118 | - multiline_parameters_brackets 119 | - nimble_operator 120 | - no_extension_access_modifier 121 | - no_grouping_extension 122 | - nslocalizedstring_key 123 | - nslocalizedstring_require_bundle 124 | - number_separator 125 | - object_literal 126 | - operator_usage_whitespace 127 | - optional_enum_case_matching 128 | - overridden_super_call 129 | - override_in_extension 130 | - pattern_matching_keywords 131 | - prefer_self_type_over_type_of_self 132 | - prefixed_toplevel_constant 133 | - private_action 134 | - private_outlet 135 | - prohibited_interface_builder 136 | - prohibited_super_call 137 | - quick_discouraged_call 138 | - quick_discouraged_focused_test 139 | - quick_discouraged_pending_test 140 | - raw_value_for_camel_cased_codable_enum 141 | - reduce_into 142 | - redundant_nil_coalescing 143 | - redundant_type_annotation 144 | - required_deinit 145 | - required_enum_case 146 | - single_test_class 147 | - sorted_first_last 148 | - sorted_imports 149 | - static_operator 150 | - strict_fileprivate 151 | - strong_iboutlet 152 | - switch_case_on_newline 153 | - toggle_bool 154 | - trailing_closure 155 | - type_contents_order 156 | - unavailable_function 157 | - unneeded_parentheses_in_closure_argument 158 | - unowned_variable_capture 159 | - untyped_error_in_catch 160 | - unused_declaration 161 | - unused_import 162 | - vertical_parameter_alignment_on_call 163 | - vertical_whitespace_between_cases 164 | - vertical_whitespace_closing_braces 165 | - vertical_whitespace_opening_braces 166 | - xct_specific_matcher 167 | - yoda_condition 168 | 169 | included: 170 | - ImageTransition 171 | - Demo 172 | 173 | # Lint対象から除外するパス 174 | excluded: 175 | 176 | custom_rules: 177 | final_class: 178 | included: "./.*\\.swift" 179 | name: "Final Class" 180 | regex: "^(private |fileprivate |internal |public |open )class" 181 | message: "Final Class" 182 | severity: error 183 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ImageTransition.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "ImageTransition" 3 | spec.version = "0.5.0" 4 | spec.summary = "ImageTransition is a library for smooth animation of images during transitions." 5 | spec.homepage = "https://github.com/shtnkgm/ImageTransition" 6 | spec.license = { :type => "MIT", :file => "LICENSE" } 7 | spec.author = "shtnkgm" 8 | spec.platform = :ios, "12.0" 9 | spec.swift_version = "5.5" 10 | spec.source = { :git => "https://github.com/shtnkgm/ImageTransition.git", :tag => "#{spec.version}" } 11 | spec.source_files = "ImageTransition/**/*.swift" 12 | end 13 | -------------------------------------------------------------------------------- /ImageTransition.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 974D5004224683130015B768 /* ItemListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974D5002224683130015B768 /* ItemListViewController.swift */; }; 11 | 974D50072246849C0015B768 /* ItemListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 974D50062246849C0015B768 /* ItemListViewController.xib */; }; 12 | 974D500A2246853B0015B768 /* ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974D50082246853B0015B768 /* ItemCell.swift */; }; 13 | 974D500B2246853B0015B768 /* ItemCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 974D50092246853B0015B768 /* ItemCell.xib */; }; 14 | 974D500D224694910015B768 /* ItemDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 974D500C224694910015B768 /* ItemDetailViewController.xib */; }; 15 | DA4B56BE216AA51D0046FC72 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA4B56BD216AA51D0046FC72 /* README.md */; }; 16 | DA5486EF21525AEB00E0899D /* ImageTransition.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5486E521525AEB00E0899D /* ImageTransition.framework */; }; 17 | DA5486F621525AEB00E0899D /* ImageTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = DA5486E821525AEB00E0899D /* ImageTransition.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | DA548700215263A900E0899D /* ImageTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5486FF215263A900E0899D /* ImageTransitioning.swift */; }; 19 | DA548702215263B400E0899D /* ImageTransitionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA548701215263B400E0899D /* ImageTransitionDelegate.swift */; }; 20 | DA548704215263BE00E0899D /* ImageTransitionable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA548703215263BE00E0899D /* ImageTransitionable.swift */; }; 21 | DA548706215263C800E0899D /* UIImageView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA548705215263C800E0899D /* UIImageView+.swift */; }; 22 | DA548708215263DB00E0899D /* UIView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA548707215263DB00E0899D /* UIView+.swift */; }; 23 | DA54870A215263E500E0899D /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA548709215263E500E0899D /* UIImage+.swift */; }; 24 | DA54870C2152644400E0899D /* UIImageView+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA54870B2152644400E0899D /* UIImageView+Tests.swift */; }; 25 | DA54870E2152644D00E0899D /* UIView+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA54870D2152644D00E0899D /* UIView+Tests.swift */; }; 26 | DA5487102152646000E0899D /* ImageTransitionDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA54870F2152646000E0899D /* ImageTransitionDelegateTests.swift */; }; 27 | DA5487122152646A00E0899D /* ImageTransitionAnimatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5487112152646A00E0899D /* ImageTransitionAnimatorTests.swift */; }; 28 | DA548714215264A800E0899D /* UIImage+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA548713215264A800E0899D /* UIImage+Tests.swift */; }; 29 | DA54871C215266AB00E0899D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA54871B215266AB00E0899D /* AppDelegate.swift */; }; 30 | DA548723215266AC00E0899D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA548722215266AC00E0899D /* Assets.xcassets */; }; 31 | DA548726215266AC00E0899D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA548724215266AC00E0899D /* LaunchScreen.storyboard */; }; 32 | DA8EAFC22152AECD00BC5968 /* ItemDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA8EAFC02152AECD00BC5968 /* ItemDetailViewController.swift */; }; 33 | DA8EAFC92152B0A700BC5968 /* ImageTransition.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5486E521525AEB00E0899D /* ImageTransition.framework */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | DA5486F021525AEB00E0899D /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = DA5486DC21525AEB00E0899D /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = DA5486E421525AEB00E0899D; 42 | remoteInfo = ImageTransition; 43 | }; 44 | DA8EAFCA2152B0CA00BC5968 /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = DA5486DC21525AEB00E0899D /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = DA5486E421525AEB00E0899D; 49 | remoteInfo = ImageTransition; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | 974D5002224683130015B768 /* ItemListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListViewController.swift; sourceTree = ""; }; 55 | 974D50062246849C0015B768 /* ItemListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemListViewController.xib; sourceTree = ""; }; 56 | 974D50082246853B0015B768 /* ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = ""; }; 57 | 974D50092246853B0015B768 /* ItemCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemCell.xib; sourceTree = ""; }; 58 | 974D500C224694910015B768 /* ItemDetailViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemDetailViewController.xib; sourceTree = ""; }; 59 | DA4B56BD216AA51D0046FC72 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 60 | DA5486E521525AEB00E0899D /* ImageTransition.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ImageTransition.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | DA5486E821525AEB00E0899D /* ImageTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageTransition.h; sourceTree = ""; }; 62 | DA5486E921525AEB00E0899D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | DA5486EE21525AEB00E0899D /* ImageTransitionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageTransitionTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | DA5486F521525AEB00E0899D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | DA5486FF215263A900E0899D /* ImageTransitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransitioning.swift; sourceTree = ""; }; 66 | DA548701215263B400E0899D /* ImageTransitionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransitionDelegate.swift; sourceTree = ""; }; 67 | DA548703215263BE00E0899D /* ImageTransitionable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransitionable.swift; sourceTree = ""; }; 68 | DA548705215263C800E0899D /* UIImageView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+.swift"; sourceTree = ""; }; 69 | DA548707215263DB00E0899D /* UIView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+.swift"; sourceTree = ""; }; 70 | DA548709215263E500E0899D /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; }; 71 | DA54870B2152644400E0899D /* UIImageView+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Tests.swift"; sourceTree = ""; }; 72 | DA54870D2152644D00E0899D /* UIView+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Tests.swift"; sourceTree = ""; }; 73 | DA54870F2152646000E0899D /* ImageTransitionDelegateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransitionDelegateTests.swift; sourceTree = ""; }; 74 | DA5487112152646A00E0899D /* ImageTransitionAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransitionAnimatorTests.swift; sourceTree = ""; }; 75 | DA548713215264A800E0899D /* UIImage+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Tests.swift"; sourceTree = ""; }; 76 | DA548719215266AB00E0899D /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | DA54871B215266AB00E0899D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 78 | DA548722215266AC00E0899D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 79 | DA548725215266AC00E0899D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 80 | DA548727215266AC00E0899D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | DA8EAFC02152AECD00BC5968 /* ItemDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailViewController.swift; sourceTree = ""; }; 82 | /* End PBXFileReference section */ 83 | 84 | /* Begin PBXFrameworksBuildPhase section */ 85 | DA5486E221525AEB00E0899D /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | DA5486EB21525AEB00E0899D /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | DA5486EF21525AEB00E0899D /* ImageTransition.framework in Frameworks */, 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | DA548716215266AB00E0899D /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | DA8EAFC92152B0A700BC5968 /* ImageTransition.framework in Frameworks */, 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | /* End PBXFrameworksBuildPhase section */ 109 | 110 | /* Begin PBXGroup section */ 111 | DA5486DB21525AEB00E0899D = { 112 | isa = PBXGroup; 113 | children = ( 114 | DA4B56BD216AA51D0046FC72 /* README.md */, 115 | DA5486E721525AEB00E0899D /* ImageTransition */, 116 | DA5486F221525AEB00E0899D /* ImageTransitionTests */, 117 | DA54871A215266AB00E0899D /* Sample */, 118 | DA5486E621525AEB00E0899D /* Products */, 119 | DA8EAFC82152B0A700BC5968 /* Frameworks */, 120 | ); 121 | sourceTree = ""; 122 | }; 123 | DA5486E621525AEB00E0899D /* Products */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | DA5486E521525AEB00E0899D /* ImageTransition.framework */, 127 | DA5486EE21525AEB00E0899D /* ImageTransitionTests.xctest */, 128 | DA548719215266AB00E0899D /* Sample.app */, 129 | ); 130 | name = Products; 131 | sourceTree = ""; 132 | }; 133 | DA5486E721525AEB00E0899D /* ImageTransition */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | DA5486E821525AEB00E0899D /* ImageTransition.h */, 137 | DA5486FF215263A900E0899D /* ImageTransitioning.swift */, 138 | DA548701215263B400E0899D /* ImageTransitionDelegate.swift */, 139 | DA548703215263BE00E0899D /* ImageTransitionable.swift */, 140 | DA548705215263C800E0899D /* UIImageView+.swift */, 141 | DA548707215263DB00E0899D /* UIView+.swift */, 142 | DA548709215263E500E0899D /* UIImage+.swift */, 143 | DA5486E921525AEB00E0899D /* Info.plist */, 144 | ); 145 | path = ImageTransition; 146 | sourceTree = ""; 147 | }; 148 | DA5486F221525AEB00E0899D /* ImageTransitionTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | DA54870B2152644400E0899D /* UIImageView+Tests.swift */, 152 | DA54870D2152644D00E0899D /* UIView+Tests.swift */, 153 | DA548713215264A800E0899D /* UIImage+Tests.swift */, 154 | DA54870F2152646000E0899D /* ImageTransitionDelegateTests.swift */, 155 | DA5487112152646A00E0899D /* ImageTransitionAnimatorTests.swift */, 156 | DA5486F521525AEB00E0899D /* Info.plist */, 157 | ); 158 | path = ImageTransitionTests; 159 | sourceTree = ""; 160 | }; 161 | DA54871A215266AB00E0899D /* Sample */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | DA54871B215266AB00E0899D /* AppDelegate.swift */, 165 | 974D5002224683130015B768 /* ItemListViewController.swift */, 166 | 974D50062246849C0015B768 /* ItemListViewController.xib */, 167 | 974D50082246853B0015B768 /* ItemCell.swift */, 168 | 974D50092246853B0015B768 /* ItemCell.xib */, 169 | DA8EAFC02152AECD00BC5968 /* ItemDetailViewController.swift */, 170 | 974D500C224694910015B768 /* ItemDetailViewController.xib */, 171 | DA548722215266AC00E0899D /* Assets.xcassets */, 172 | DA548724215266AC00E0899D /* LaunchScreen.storyboard */, 173 | DA548727215266AC00E0899D /* Info.plist */, 174 | ); 175 | path = Sample; 176 | sourceTree = ""; 177 | }; 178 | DA8EAFC82152B0A700BC5968 /* Frameworks */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | ); 182 | name = Frameworks; 183 | sourceTree = ""; 184 | }; 185 | /* End PBXGroup section */ 186 | 187 | /* Begin PBXHeadersBuildPhase section */ 188 | DA5486E021525AEB00E0899D /* Headers */ = { 189 | isa = PBXHeadersBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | DA5486F621525AEB00E0899D /* ImageTransition.h in Headers */, 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | /* End PBXHeadersBuildPhase section */ 197 | 198 | /* Begin PBXNativeTarget section */ 199 | DA5486E421525AEB00E0899D /* ImageTransition */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = DA5486F921525AEB00E0899D /* Build configuration list for PBXNativeTarget "ImageTransition" */; 202 | buildPhases = ( 203 | DA5486E021525AEB00E0899D /* Headers */, 204 | DA5486E121525AEB00E0899D /* Sources */, 205 | DA5486E221525AEB00E0899D /* Frameworks */, 206 | DA5486E321525AEB00E0899D /* Resources */, 207 | DA54872B215268DF00E0899D /* SwiftLint */, 208 | ); 209 | buildRules = ( 210 | ); 211 | dependencies = ( 212 | ); 213 | name = ImageTransition; 214 | productName = ImageTransition; 215 | productReference = DA5486E521525AEB00E0899D /* ImageTransition.framework */; 216 | productType = "com.apple.product-type.framework"; 217 | }; 218 | DA5486ED21525AEB00E0899D /* ImageTransitionTests */ = { 219 | isa = PBXNativeTarget; 220 | buildConfigurationList = DA5486FC21525AEB00E0899D /* Build configuration list for PBXNativeTarget "ImageTransitionTests" */; 221 | buildPhases = ( 222 | DA5486EA21525AEB00E0899D /* Sources */, 223 | DA5486EB21525AEB00E0899D /* Frameworks */, 224 | DA5486EC21525AEB00E0899D /* Resources */, 225 | ); 226 | buildRules = ( 227 | ); 228 | dependencies = ( 229 | DA5486F121525AEB00E0899D /* PBXTargetDependency */, 230 | ); 231 | name = ImageTransitionTests; 232 | productName = ImageTransitionTests; 233 | productReference = DA5486EE21525AEB00E0899D /* ImageTransitionTests.xctest */; 234 | productType = "com.apple.product-type.bundle.unit-test"; 235 | }; 236 | DA548718215266AB00E0899D /* Sample */ = { 237 | isa = PBXNativeTarget; 238 | buildConfigurationList = DA548728215266AC00E0899D /* Build configuration list for PBXNativeTarget "Sample" */; 239 | buildPhases = ( 240 | DA548715215266AB00E0899D /* Sources */, 241 | DA548716215266AB00E0899D /* Frameworks */, 242 | DA548717215266AB00E0899D /* Resources */, 243 | ); 244 | buildRules = ( 245 | ); 246 | dependencies = ( 247 | DA8EAFCB2152B0CA00BC5968 /* PBXTargetDependency */, 248 | ); 249 | name = Sample; 250 | productName = Sample; 251 | productReference = DA548719215266AB00E0899D /* Sample.app */; 252 | productType = "com.apple.product-type.application"; 253 | }; 254 | /* End PBXNativeTarget section */ 255 | 256 | /* Begin PBXProject section */ 257 | DA5486DC21525AEB00E0899D /* Project object */ = { 258 | isa = PBXProject; 259 | attributes = { 260 | LastSwiftUpdateCheck = 1000; 261 | LastUpgradeCheck = 1300; 262 | ORGANIZATIONNAME = "Shota Nakagami"; 263 | TargetAttributes = { 264 | DA5486E421525AEB00E0899D = { 265 | CreatedOnToolsVersion = 10.0; 266 | LastSwiftMigration = 1000; 267 | }; 268 | DA5486ED21525AEB00E0899D = { 269 | CreatedOnToolsVersion = 10.0; 270 | }; 271 | DA548718215266AB00E0899D = { 272 | CreatedOnToolsVersion = 10.0; 273 | }; 274 | }; 275 | }; 276 | buildConfigurationList = DA5486DF21525AEB00E0899D /* Build configuration list for PBXProject "ImageTransition" */; 277 | compatibilityVersion = "Xcode 9.3"; 278 | developmentRegion = en; 279 | hasScannedForEncodings = 0; 280 | knownRegions = ( 281 | en, 282 | Base, 283 | ); 284 | mainGroup = DA5486DB21525AEB00E0899D; 285 | productRefGroup = DA5486E621525AEB00E0899D /* Products */; 286 | projectDirPath = ""; 287 | projectRoot = ""; 288 | targets = ( 289 | DA5486E421525AEB00E0899D /* ImageTransition */, 290 | DA5486ED21525AEB00E0899D /* ImageTransitionTests */, 291 | DA548718215266AB00E0899D /* Sample */, 292 | ); 293 | }; 294 | /* End PBXProject section */ 295 | 296 | /* Begin PBXResourcesBuildPhase section */ 297 | DA5486E321525AEB00E0899D /* Resources */ = { 298 | isa = PBXResourcesBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | DA4B56BE216AA51D0046FC72 /* README.md in Resources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | DA5486EC21525AEB00E0899D /* Resources */ = { 306 | isa = PBXResourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | DA548717215266AB00E0899D /* Resources */ = { 313 | isa = PBXResourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | 974D50072246849C0015B768 /* ItemListViewController.xib in Resources */, 317 | 974D500D224694910015B768 /* ItemDetailViewController.xib in Resources */, 318 | 974D500B2246853B0015B768 /* ItemCell.xib in Resources */, 319 | DA548726215266AC00E0899D /* LaunchScreen.storyboard in Resources */, 320 | DA548723215266AC00E0899D /* Assets.xcassets in Resources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXResourcesBuildPhase section */ 325 | 326 | /* Begin PBXShellScriptBuildPhase section */ 327 | DA54872B215268DF00E0899D /* SwiftLint */ = { 328 | isa = PBXShellScriptBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | ); 332 | inputFileListPaths = ( 333 | ); 334 | inputPaths = ( 335 | ); 336 | name = SwiftLint; 337 | outputFileListPaths = ( 338 | ); 339 | outputPaths = ( 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | shellPath = /bin/sh; 343 | shellScript = "if [ \"${CONFIGURATION}\" = \"Debug\" ]; then\n xcrun --sdk macosx mint run swiftlint autocorrect --format\n xcrun --sdk macosx mint run swiftlint\nfi\n"; 344 | }; 345 | /* End PBXShellScriptBuildPhase section */ 346 | 347 | /* Begin PBXSourcesBuildPhase section */ 348 | DA5486E121525AEB00E0899D /* Sources */ = { 349 | isa = PBXSourcesBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | DA548706215263C800E0899D /* UIImageView+.swift in Sources */, 353 | DA548700215263A900E0899D /* ImageTransitioning.swift in Sources */, 354 | DA548704215263BE00E0899D /* ImageTransitionable.swift in Sources */, 355 | DA54870A215263E500E0899D /* UIImage+.swift in Sources */, 356 | DA548702215263B400E0899D /* ImageTransitionDelegate.swift in Sources */, 357 | DA548708215263DB00E0899D /* UIView+.swift in Sources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | DA5486EA21525AEB00E0899D /* Sources */ = { 362 | isa = PBXSourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | DA54870C2152644400E0899D /* UIImageView+Tests.swift in Sources */, 366 | DA5487122152646A00E0899D /* ImageTransitionAnimatorTests.swift in Sources */, 367 | DA54870E2152644D00E0899D /* UIView+Tests.swift in Sources */, 368 | DA5487102152646000E0899D /* ImageTransitionDelegateTests.swift in Sources */, 369 | DA548714215264A800E0899D /* UIImage+Tests.swift in Sources */, 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | DA548715215266AB00E0899D /* Sources */ = { 374 | isa = PBXSourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | DA54871C215266AB00E0899D /* AppDelegate.swift in Sources */, 378 | 974D5004224683130015B768 /* ItemListViewController.swift in Sources */, 379 | DA8EAFC22152AECD00BC5968 /* ItemDetailViewController.swift in Sources */, 380 | 974D500A2246853B0015B768 /* ItemCell.swift in Sources */, 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | /* End PBXSourcesBuildPhase section */ 385 | 386 | /* Begin PBXTargetDependency section */ 387 | DA5486F121525AEB00E0899D /* PBXTargetDependency */ = { 388 | isa = PBXTargetDependency; 389 | target = DA5486E421525AEB00E0899D /* ImageTransition */; 390 | targetProxy = DA5486F021525AEB00E0899D /* PBXContainerItemProxy */; 391 | }; 392 | DA8EAFCB2152B0CA00BC5968 /* PBXTargetDependency */ = { 393 | isa = PBXTargetDependency; 394 | target = DA5486E421525AEB00E0899D /* ImageTransition */; 395 | targetProxy = DA8EAFCA2152B0CA00BC5968 /* PBXContainerItemProxy */; 396 | }; 397 | /* End PBXTargetDependency section */ 398 | 399 | /* Begin PBXVariantGroup section */ 400 | DA548724215266AC00E0899D /* LaunchScreen.storyboard */ = { 401 | isa = PBXVariantGroup; 402 | children = ( 403 | DA548725215266AC00E0899D /* Base */, 404 | ); 405 | name = LaunchScreen.storyboard; 406 | sourceTree = ""; 407 | }; 408 | /* End PBXVariantGroup section */ 409 | 410 | /* Begin XCBuildConfiguration section */ 411 | DA5486F721525AEB00E0899D /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ALWAYS_SEARCH_USER_PATHS = NO; 415 | CLANG_ANALYZER_NONNULL = YES; 416 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 417 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 418 | CLANG_CXX_LIBRARY = "libc++"; 419 | CLANG_ENABLE_MODULES = YES; 420 | CLANG_ENABLE_OBJC_ARC = YES; 421 | CLANG_ENABLE_OBJC_WEAK = YES; 422 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 423 | CLANG_WARN_BOOL_CONVERSION = YES; 424 | CLANG_WARN_COMMA = YES; 425 | CLANG_WARN_CONSTANT_CONVERSION = YES; 426 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 427 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 428 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 429 | CLANG_WARN_EMPTY_BODY = YES; 430 | CLANG_WARN_ENUM_CONVERSION = YES; 431 | CLANG_WARN_INFINITE_RECURSION = YES; 432 | CLANG_WARN_INT_CONVERSION = YES; 433 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 434 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 435 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 437 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 438 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 439 | CLANG_WARN_STRICT_PROTOTYPES = YES; 440 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 441 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | CODE_SIGN_IDENTITY = ""; 445 | COPY_PHASE_STRIP = NO; 446 | CURRENT_PROJECT_VERSION = 1; 447 | DEBUG_INFORMATION_FORMAT = dwarf; 448 | ENABLE_STRICT_OBJC_MSGSEND = YES; 449 | ENABLE_TESTABILITY = YES; 450 | GCC_C_LANGUAGE_STANDARD = gnu11; 451 | GCC_DYNAMIC_NO_PIC = NO; 452 | GCC_NO_COMMON_BLOCKS = YES; 453 | GCC_OPTIMIZATION_LEVEL = 0; 454 | GCC_PREPROCESSOR_DEFINITIONS = ( 455 | "DEBUG=1", 456 | "$(inherited)", 457 | ); 458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 460 | GCC_WARN_UNDECLARED_SELECTOR = YES; 461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 462 | GCC_WARN_UNUSED_FUNCTION = YES; 463 | GCC_WARN_UNUSED_VARIABLE = YES; 464 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 465 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 466 | MTL_FAST_MATH = YES; 467 | ONLY_ACTIVE_ARCH = YES; 468 | SDKROOT = iphoneos; 469 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 470 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 471 | SWIFT_VERSION = 5.0; 472 | VERSIONING_SYSTEM = "apple-generic"; 473 | VERSION_INFO_PREFIX = ""; 474 | }; 475 | name = Debug; 476 | }; 477 | DA5486F821525AEB00E0899D /* Release */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ALWAYS_SEARCH_USER_PATHS = NO; 481 | CLANG_ANALYZER_NONNULL = YES; 482 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 483 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 484 | CLANG_CXX_LIBRARY = "libc++"; 485 | CLANG_ENABLE_MODULES = YES; 486 | CLANG_ENABLE_OBJC_ARC = YES; 487 | CLANG_ENABLE_OBJC_WEAK = YES; 488 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 489 | CLANG_WARN_BOOL_CONVERSION = YES; 490 | CLANG_WARN_COMMA = YES; 491 | CLANG_WARN_CONSTANT_CONVERSION = YES; 492 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 493 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 494 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 495 | CLANG_WARN_EMPTY_BODY = YES; 496 | CLANG_WARN_ENUM_CONVERSION = YES; 497 | CLANG_WARN_INFINITE_RECURSION = YES; 498 | CLANG_WARN_INT_CONVERSION = YES; 499 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 500 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 501 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 502 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 503 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 504 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 505 | CLANG_WARN_STRICT_PROTOTYPES = YES; 506 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 507 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 508 | CLANG_WARN_UNREACHABLE_CODE = YES; 509 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 510 | CODE_SIGN_IDENTITY = ""; 511 | COPY_PHASE_STRIP = NO; 512 | CURRENT_PROJECT_VERSION = 1; 513 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 514 | ENABLE_NS_ASSERTIONS = NO; 515 | ENABLE_STRICT_OBJC_MSGSEND = YES; 516 | GCC_C_LANGUAGE_STANDARD = gnu11; 517 | GCC_NO_COMMON_BLOCKS = YES; 518 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 519 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 520 | GCC_WARN_UNDECLARED_SELECTOR = YES; 521 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 522 | GCC_WARN_UNUSED_FUNCTION = YES; 523 | GCC_WARN_UNUSED_VARIABLE = YES; 524 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 525 | MTL_ENABLE_DEBUG_INFO = NO; 526 | MTL_FAST_MATH = YES; 527 | SDKROOT = iphoneos; 528 | SWIFT_COMPILATION_MODE = wholemodule; 529 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 530 | SWIFT_VERSION = 5.0; 531 | VALIDATE_PRODUCT = YES; 532 | VERSIONING_SYSTEM = "apple-generic"; 533 | VERSION_INFO_PREFIX = ""; 534 | }; 535 | name = Release; 536 | }; 537 | DA5486FA21525AEB00E0899D /* Debug */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | CLANG_ENABLE_MODULES = YES; 541 | CODE_SIGN_IDENTITY = ""; 542 | CODE_SIGN_STYLE = Automatic; 543 | DEFINES_MODULE = YES; 544 | DEVELOPMENT_TEAM = ""; 545 | DYLIB_COMPATIBILITY_VERSION = 1; 546 | DYLIB_CURRENT_VERSION = 1; 547 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 548 | INFOPLIST_FILE = ImageTransition/Info.plist; 549 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 550 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 551 | LD_RUNPATH_SEARCH_PATHS = ( 552 | "$(inherited)", 553 | "@executable_path/Frameworks", 554 | "@loader_path/Frameworks", 555 | ); 556 | PRODUCT_BUNDLE_IDENTIFIER = com.github.shtnkgm.ImageTransition; 557 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 558 | SKIP_INSTALL = YES; 559 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 560 | SWIFT_VERSION = 5.0; 561 | TARGETED_DEVICE_FAMILY = "1,2"; 562 | }; 563 | name = Debug; 564 | }; 565 | DA5486FB21525AEB00E0899D /* Release */ = { 566 | isa = XCBuildConfiguration; 567 | buildSettings = { 568 | CLANG_ENABLE_MODULES = YES; 569 | CODE_SIGN_IDENTITY = ""; 570 | CODE_SIGN_STYLE = Automatic; 571 | DEFINES_MODULE = YES; 572 | DEVELOPMENT_TEAM = ""; 573 | DYLIB_COMPATIBILITY_VERSION = 1; 574 | DYLIB_CURRENT_VERSION = 1; 575 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 576 | INFOPLIST_FILE = ImageTransition/Info.plist; 577 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 578 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 579 | LD_RUNPATH_SEARCH_PATHS = ( 580 | "$(inherited)", 581 | "@executable_path/Frameworks", 582 | "@loader_path/Frameworks", 583 | ); 584 | PRODUCT_BUNDLE_IDENTIFIER = com.github.shtnkgm.ImageTransition; 585 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 586 | SKIP_INSTALL = YES; 587 | SWIFT_VERSION = 5.0; 588 | TARGETED_DEVICE_FAMILY = "1,2"; 589 | }; 590 | name = Release; 591 | }; 592 | DA5486FD21525AEB00E0899D /* Debug */ = { 593 | isa = XCBuildConfiguration; 594 | buildSettings = { 595 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 596 | CODE_SIGN_IDENTITY = ""; 597 | CODE_SIGN_STYLE = Automatic; 598 | DEVELOPMENT_TEAM = ""; 599 | INFOPLIST_FILE = ImageTransitionTests/Info.plist; 600 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 601 | LD_RUNPATH_SEARCH_PATHS = ( 602 | "$(inherited)", 603 | "@executable_path/Frameworks", 604 | "@loader_path/Frameworks", 605 | ); 606 | PRODUCT_BUNDLE_IDENTIFIER = com.shotanakagami.ImageTransitionTests; 607 | PRODUCT_NAME = "$(TARGET_NAME)"; 608 | SWIFT_VERSION = 5.0; 609 | TARGETED_DEVICE_FAMILY = "1,2"; 610 | }; 611 | name = Debug; 612 | }; 613 | DA5486FE21525AEB00E0899D /* Release */ = { 614 | isa = XCBuildConfiguration; 615 | buildSettings = { 616 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 617 | CODE_SIGN_IDENTITY = ""; 618 | CODE_SIGN_STYLE = Automatic; 619 | DEVELOPMENT_TEAM = ""; 620 | INFOPLIST_FILE = ImageTransitionTests/Info.plist; 621 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 622 | LD_RUNPATH_SEARCH_PATHS = ( 623 | "$(inherited)", 624 | "@executable_path/Frameworks", 625 | "@loader_path/Frameworks", 626 | ); 627 | PRODUCT_BUNDLE_IDENTIFIER = com.shotanakagami.ImageTransitionTests; 628 | PRODUCT_NAME = "$(TARGET_NAME)"; 629 | SWIFT_VERSION = 5.0; 630 | TARGETED_DEVICE_FAMILY = "1,2"; 631 | }; 632 | name = Release; 633 | }; 634 | DA548729215266AC00E0899D /* Debug */ = { 635 | isa = XCBuildConfiguration; 636 | buildSettings = { 637 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 638 | CODE_SIGN_IDENTITY = ""; 639 | CODE_SIGN_STYLE = Automatic; 640 | DEVELOPMENT_TEAM = ""; 641 | INFOPLIST_FILE = Sample/Info.plist; 642 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 643 | LD_RUNPATH_SEARCH_PATHS = ( 644 | "$(inherited)", 645 | "@executable_path/Frameworks", 646 | ); 647 | PRODUCT_BUNDLE_IDENTIFIER = com.github.shtnkgm.Sample; 648 | PRODUCT_NAME = "$(TARGET_NAME)"; 649 | SWIFT_VERSION = 5.0; 650 | TARGETED_DEVICE_FAMILY = "1,2"; 651 | }; 652 | name = Debug; 653 | }; 654 | DA54872A215266AC00E0899D /* Release */ = { 655 | isa = XCBuildConfiguration; 656 | buildSettings = { 657 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 658 | CODE_SIGN_IDENTITY = ""; 659 | CODE_SIGN_STYLE = Automatic; 660 | DEVELOPMENT_TEAM = ""; 661 | INFOPLIST_FILE = Sample/Info.plist; 662 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 663 | LD_RUNPATH_SEARCH_PATHS = ( 664 | "$(inherited)", 665 | "@executable_path/Frameworks", 666 | ); 667 | PRODUCT_BUNDLE_IDENTIFIER = com.github.shtnkgm.Sample; 668 | PRODUCT_NAME = "$(TARGET_NAME)"; 669 | SWIFT_VERSION = 5.0; 670 | TARGETED_DEVICE_FAMILY = "1,2"; 671 | }; 672 | name = Release; 673 | }; 674 | /* End XCBuildConfiguration section */ 675 | 676 | /* Begin XCConfigurationList section */ 677 | DA5486DF21525AEB00E0899D /* Build configuration list for PBXProject "ImageTransition" */ = { 678 | isa = XCConfigurationList; 679 | buildConfigurations = ( 680 | DA5486F721525AEB00E0899D /* Debug */, 681 | DA5486F821525AEB00E0899D /* Release */, 682 | ); 683 | defaultConfigurationIsVisible = 0; 684 | defaultConfigurationName = Release; 685 | }; 686 | DA5486F921525AEB00E0899D /* Build configuration list for PBXNativeTarget "ImageTransition" */ = { 687 | isa = XCConfigurationList; 688 | buildConfigurations = ( 689 | DA5486FA21525AEB00E0899D /* Debug */, 690 | DA5486FB21525AEB00E0899D /* Release */, 691 | ); 692 | defaultConfigurationIsVisible = 0; 693 | defaultConfigurationName = Release; 694 | }; 695 | DA5486FC21525AEB00E0899D /* Build configuration list for PBXNativeTarget "ImageTransitionTests" */ = { 696 | isa = XCConfigurationList; 697 | buildConfigurations = ( 698 | DA5486FD21525AEB00E0899D /* Debug */, 699 | DA5486FE21525AEB00E0899D /* Release */, 700 | ); 701 | defaultConfigurationIsVisible = 0; 702 | defaultConfigurationName = Release; 703 | }; 704 | DA548728215266AC00E0899D /* Build configuration list for PBXNativeTarget "Sample" */ = { 705 | isa = XCConfigurationList; 706 | buildConfigurations = ( 707 | DA548729215266AC00E0899D /* Debug */, 708 | DA54872A215266AC00E0899D /* Release */, 709 | ); 710 | defaultConfigurationIsVisible = 0; 711 | defaultConfigurationName = Release; 712 | }; 713 | /* End XCConfigurationList section */ 714 | }; 715 | rootObject = DA5486DC21525AEB00E0899D /* Project object */; 716 | } 717 | -------------------------------------------------------------------------------- /ImageTransition.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ImageTransition.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ImageTransition.xcodeproj/xcshareddata/xcschemes/ImageTransition.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /ImageTransition.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /ImageTransition/ImageTransition.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransition.h 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ImageTransition. 12 | FOUNDATION_EXPORT double ImageTransitionVersionNumber; 13 | 14 | //! Project version string for ImageTransition. 15 | FOUNDATION_EXPORT const unsigned char ImageTransitionVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ImageTransition/ImageTransitionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransitionDelegate.swift 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public final class ImageTransitionDelegate: NSObject { 12 | public static let shared = ImageTransitionDelegate() 13 | 14 | public var presentDuration: TimeInterval = 0.375 15 | public var dismissDuration: TimeInterval = 0.375 16 | public var pushDuration: TimeInterval = 0.375 17 | public var popDuration: TimeInterval = 0.375 18 | public var presentAnimationOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseInOut] 19 | public var dismissAnimationOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseInOut] 20 | public var pushAnimationOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseInOut] 21 | public var popAnimationOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseInOut] 22 | 23 | private override init() { } 24 | } 25 | 26 | extension ImageTransitionDelegate: UIViewControllerTransitioningDelegate { 27 | public func animationController( 28 | forPresented presented: UIViewController, 29 | presenting: UIViewController, 30 | source: UIViewController 31 | ) -> UIViewControllerAnimatedTransitioning? { 32 | ImageTransitioning(duration: presentDuration, animationOptions: presentAnimationOptions) 33 | } 34 | 35 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 36 | ImageTransitioning(duration: dismissDuration, animationOptions: dismissAnimationOptions) 37 | } 38 | } 39 | 40 | extension ImageTransitionDelegate: UINavigationControllerDelegate { 41 | public func navigationController( 42 | _ navigationController: UINavigationController, 43 | animationControllerFor operation: UINavigationController.Operation, 44 | from fromVC: UIViewController, 45 | to toVC: UIViewController 46 | ) -> UIViewControllerAnimatedTransitioning? { 47 | switch operation { 48 | case .none: 49 | return nil 50 | 51 | case .pop: 52 | return ImageTransitioning(duration: popDuration, animationOptions: popAnimationOptions) 53 | 54 | case .push: 55 | return ImageTransitioning(duration: pushDuration, animationOptions: pushAnimationOptions) 56 | 57 | @unknown default: 58 | fatalError() 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ImageTransition/ImageTransitionable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransitionable.swift 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ImageTransitionable { 12 | var imageViewForTransition: UIImageView? { get } 13 | } 14 | -------------------------------------------------------------------------------- /ImageTransition/ImageTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransitioning.swift 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal final class ImageTransitioning: NSObject, UIViewControllerAnimatedTransitioning { 12 | private let duration: TimeInterval 13 | private let animationOptions: UIView.AnimationOptions 14 | 15 | internal init(duration: TimeInterval, animationOptions: UIView.AnimationOptions) { 16 | self.duration = duration 17 | self.animationOptions = animationOptions 18 | } 19 | 20 | internal func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 21 | duration 22 | } 23 | 24 | internal func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 25 | guard let fromVC = transitionContext.viewController(forKey: .from) else { 26 | assertionFailure("fromVC is nil") 27 | return 28 | } 29 | guard let toVC = transitionContext.viewController(forKey: .to) else { 30 | assertionFailure("toVC is nil") 31 | return 32 | } 33 | guard let fromImageView = (fromVC as? ImageTransitionable)?.imageViewForTransition else { 34 | assertionFailure("fromImageView is nil") 35 | return 36 | } 37 | guard let toImageView = (toVC as? ImageTransitionable)?.imageViewForTransition else { 38 | assertionFailure("toImageView is nil") 39 | return 40 | } 41 | guard let fromImage = fromImageView.image else { 42 | assertionFailure("fromImage is nil") 43 | return 44 | } 45 | guard let toImage = toImageView.image else { 46 | assertionFailure("toImage is nil") 47 | return 48 | } 49 | 50 | // Use image with larger size 51 | let movingView = UIImageView(image: fromImage.largerCompared(with: toImage)) 52 | movingView.clipsToBounds = true 53 | movingView.contentMode = .scaleAspectFill 54 | movingView.frame.size = fromImageView.displayingImageSize 55 | movingView.center = fromImageView.convertCenter(to: fromVC.view) 56 | movingView.layer.cornerRadius = fromImageView.layer.cornerRadius 57 | 58 | transitionContext.containerView.addSubviews(toVC.view, movingView) 59 | 60 | // Do not use "isHidden" not to animate in stackview 61 | fromImageView.alpha = 0.0 62 | toImageView.alpha = 0.0 63 | toVC.view.alpha = 0.0 64 | 65 | // To calculate displayImageSize correctly, recalculate the layout 66 | toVC.view.setNeedsLayout() 67 | toVC.view.layoutIfNeeded() 68 | 69 | let duration = transitionDuration(using: transitionContext) 70 | let options: UIView.AnimationOptions = animationOptions 71 | UIView.animate(withDuration: duration, delay: 0, options: options, animations: { 72 | toVC.view.alpha = 1.0 73 | movingView.frame.size = toImageView.displayingImageSize 74 | movingView.center = toImageView.convertCenter(to: toVC.view) 75 | movingView.layer.cornerRadius = toImageView.layer.cornerRadius 76 | }, completion: { _ in 77 | // Do not use "isHidden" not to animate in stackview 78 | fromImageView.alpha = 1.0 79 | toImageView.alpha = 1.0 80 | movingView.removeFromSuperview() 81 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ImageTransition/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /ImageTransition/UIImage+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+.swift 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal extension UIImage { 12 | var area: CGFloat { 13 | size.width * size.height 14 | } 15 | 16 | func largerCompared(with comparedImage: UIImage) -> UIImage { 17 | area > comparedImage.area ? self : comparedImage 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ImageTransition/UIImageView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+.swift 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import AVFoundation 10 | import UIKit 11 | 12 | internal extension UIImageView { 13 | var displayingImageSize: CGSize { 14 | guard let image = image else { return .zero } 15 | switch contentMode { 16 | case .scaleAspectFit: 17 | return AVMakeRect(aspectRatio: image.size, insideRect: bounds).size 18 | 19 | default: 20 | return bounds.size 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ImageTransition/UIView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+.swift 3 | // ImageTransition 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal extension UIView { 12 | func convertFrame(to view: UIView) -> CGRect { 13 | convert(bounds, to: view) 14 | } 15 | 16 | func convertCenter(to view: UIView) -> CGPoint { 17 | let frame = convertFrame(to: view) 18 | return CGPoint(x: frame.minX + frame.width / 2.0, y: frame.minY + frame.height / 2.0) 19 | } 20 | 21 | func addSubviews(_ views: UIView...) { 22 | views.forEach { 23 | addSubview($0) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ImageTransitionTests/ImageTransitionAnimatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransitionAnimatorTests.swift 3 | // ImageTransitionTests 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ImageTransition 11 | 12 | class ImageTransitionAnimatorTests: XCTestCase { 13 | func test_transitionDuration() { 14 | let expectedDuration = 3.0 15 | let imageTransitionAnimator = ImageTransitioning(duration: expectedDuration, animationOptions: .curveEaseIn) 16 | XCTAssertEqual(imageTransitionAnimator.transitionDuration(using: nil), expectedDuration) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ImageTransitionTests/ImageTransitionDelegateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransitionDelegateTests.swift 3 | // ImageTransitionTests 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ImageTransition 11 | 12 | class ImageTransitionDelegateTests: XCTestCase { 13 | func test_animationController_forPresented() { 14 | let imageTransitionDelegate = ImageTransitionDelegate.shared 15 | XCTAssertNotNil(imageTransitionDelegate.animationController(forPresented: UIViewController(), 16 | presenting: UIViewController(), 17 | source: UIViewController())) 18 | } 19 | 20 | func test_animationController_forDismissed() { 21 | let imageTransitionDelegate = ImageTransitionDelegate.shared 22 | XCTAssertNotNil(imageTransitionDelegate.animationController(forDismissed: UIViewController())) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ImageTransitionTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ImageTransitionTests/UIImage+Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Tests.swift 3 | // ImageTransitionTests 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | @testable import ImageTransition 10 | import XCTest 11 | 12 | class UIImage_Tests: XCTestCase { 13 | func test_area() { 14 | let image = UIImage.sampleImage(size: CGSize(width: 3, height: 4)) 15 | XCTAssertEqual(image.area, 12) 16 | } 17 | 18 | func test_largerCompared() { 19 | let small = UIImage.sampleImage(size: CGSize(width: 10, height: 10)) 20 | let large = UIImage.sampleImage(size: CGSize(width: 100, height: 100)) 21 | XCTAssertEqual(small.largerCompared(with: large), large) 22 | XCTAssertEqual(large.largerCompared(with: small), large) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ImageTransitionTests/UIImageView+Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Tests.swift 3 | // ImageTransitionTests 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | @testable import ImageTransition 10 | import XCTest 11 | 12 | class UIImageView_Tests: XCTestCase { 13 | func test_displayingImageSize_画像なし() { 14 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 15 | XCTAssertEqual(imageView.displayingImageSize, .zero) 16 | } 17 | 18 | func test_displayingImageSize_scaleAspectFitの場合_画像あり() { 19 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 20 | imageView.image = UIImage.sampleImage(size: CGSize(width: 50, height: 30)) 21 | imageView.contentMode = .scaleAspectFit 22 | XCTAssertEqual(imageView.displayingImageSize, CGSize(width: 100, height: 60)) 23 | } 24 | 25 | func test_displayingImageSize_scaleAspectFillの場合_画像あり() { 26 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 27 | imageView.image = UIImage.sampleImage(size: CGSize(width: 50, height: 30)) 28 | imageView.contentMode = .scaleAspectFill 29 | XCTAssertEqual(imageView.displayingImageSize, CGSize(width: 100, height: 100)) 30 | } 31 | } 32 | 33 | extension UIImage { 34 | class func sampleImage(size: CGSize) -> UIImage { 35 | UIGraphicsBeginImageContext(size) 36 | defer { UIGraphicsEndImageContext() } 37 | 38 | let rect = CGRect(origin: .zero, size: size) 39 | let context = UIGraphicsGetCurrentContext()! 40 | context.setFillColor(UIColor.white.cgColor) 41 | context.fill(rect) 42 | return UIGraphicsGetImageFromCurrentImageContext()! 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ImageTransitionTests/UIView+Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Tests.swift 3 | // ImageTransitionTests 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | @testable import ImageTransition 10 | import XCTest 11 | 12 | class UIView_Tests: XCTestCase { 13 | func test_convertFrame() { 14 | let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 1_000, height: 1_000)) 15 | let view2 = UIView(frame: CGRect(x: 100, y: 200, width: 500, height: 500)) 16 | let view3 = UIView(frame: CGRect(x: 300, y: 400, width: 200, height: 300)) 17 | let view4 = UIView(frame: CGRect(x: 10, y: 20, width: 30, height: 40)) 18 | 19 | view1.addSubview(view2) 20 | view2.addSubview(view3) 21 | view3.addSubview(view4) 22 | 23 | let expectedFrame = CGRect(x: 410, y: 620, width: 30, height: 40) 24 | 25 | XCTAssertEqual(expectedFrame, view4.convertFrame(to: view1)) 26 | } 27 | 28 | func test_convertCenter() { 29 | let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 1_000, height: 1_000)) 30 | let view2 = UIView(frame: CGRect(x: 100, y: 200, width: 500, height: 500)) 31 | let view3 = UIView(frame: CGRect(x: 300, y: 400, width: 200, height: 300)) 32 | let view4 = UIView(frame: CGRect(x: 10, y: 20, width: 30, height: 40)) 33 | 34 | view1.addSubview(view2) 35 | view2.addSubview(view3) 36 | view3.addSubview(view4) 37 | 38 | let expectedCenter = CGPoint(x: 410 + 15, y: 620 + 20) 39 | 40 | XCTAssertEqual(expectedCenter, view4.convertCenter(to: view1)) 41 | } 42 | 43 | func test_addSubviews() { 44 | let view1 = UIView() 45 | let view2 = UIView() 46 | let view3 = UIView() 47 | view1.addSubviews(view2, view3) 48 | XCTAssertTrue(view1.subviews.contains(view2)) 49 | XCTAssertTrue(view1.subviews.contains(view3)) 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Shota Nakagami 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Mintfile: -------------------------------------------------------------------------------- 1 | realm/SwiftLint@0.42.0 -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "ImageTransition", 6 | platforms: [.iOS("12.0")], 7 | products: [ 8 | .library( 9 | name: "ImageTransition", 10 | targets: ["ImageTransition"]) 11 | ], 12 | dependencies: [], 13 | targets: [ 14 | .target( 15 | name: "ImageTransition", 16 | dependencies: []), 17 | .testTarget( 18 | name: "ImageTransitionTests", 19 | dependencies: ["ImageTransition"]) 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageTransition 2 | 3 | [![Cocoapods](https://img.shields.io/cocoapods/v/ImageTransition.svg)](https://github.com/shtnkgm/ImageTransition) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | [![Swift Version](https://img.shields.io/badge/Swift-5.5-F16D39.svg)](https://developer.apple.com/swift) 6 | [![GitHub](https://img.shields.io/github/license/shtnkgm/ImageTransition.svg)](https://github.com/shtnkgm/ImageTransition/blob/master/LICENSE) 7 | 8 | **ImageTransition** is a library for smooth animation of images during transitions. 9 | 10 | Something looks like below: 11 | 12 | |e.g. UIImageView|e.g. UIImageView in UICollectionView| 13 | |:---:|:---:| 14 | |sample_01.gif | sample_02.gif| 15 | 16 | ## Feature 17 | - [x] Transition zooming animation like the iOS Photos app and the "Pinterest", and so on 18 | - [x] Easy to use (conform to `ImageTransitionable` protocol) 19 | - [x] Swifty (protocol-oriented) 20 | - [x] Animation configuration customizable (animation duration, UIView.AnimationOptions) 21 | - [x] CornerRadius animation (e.g. from a round image to a square Image) 22 | 23 | ## Installation 24 | 25 | - Swift Package Manager: `https://github.com/shtnkgm/ImageTransition.git` 26 | - Carthage: `github "shtnkgm/ImageTransition"` 27 | - CocoaPods: `pod "ImageTransition"` 28 | 29 | ## Usage 30 | 31 | - Confirm `ImageTransitionable` protocol 32 | ```swift 33 | // Source UIViewController 34 | import ImageTransition 35 | extension SourceViewController: ImageTransitionable { 36 | var imageViewForTransition: UIImageView? { 37 | return imageView 38 | } 39 | } 40 | // Destination UIViewController 41 | import ImageTransition 42 | extension DestinationViewController: ImageTransitionable { 43 | var imageViewForTransition: UIImageView? { 44 | return imageView 45 | } 46 | } 47 | ``` 48 | - Set Delegate 49 | ```swift 50 | // present / dismiss transition 51 | @objc private func imageViewDidTapped() { 52 | let destinationViewController = DestinationViewController.make() 53 | destinationViewController.transitioningDelegate = ImageTransitionDelegate.shared 54 | present(destinationViewController, animated: true, completion: nil) 55 | } 56 | 57 | // push / pop transition 58 | @objc private func imageViewDidTapped() { 59 | let destinationViewController = DestinationViewController.make() 60 | // Set ImageTransitionDelegate.shared to `delegate` property of UINavigationContoller 61 | navigationController?.delegate = ImageTransitionDelegate.shared 62 | navigationController?.pushViewController(destinationViewController, animated: true) 63 | } 64 | ``` 65 | 66 | ## Customize 67 | 68 | You can customize the configuration of animation. 69 | 70 | ```swift 71 | ImageTransitionDelegate.shared.presentDuration = 0.5 72 | ImageTransitionDelegate.shared.dismissDuration = 0.5 73 | ImageTransitionDelegate.shared.pushDuration = 0.5 74 | ImageTransitionDelegate.shared.popDuration = 0.5 75 | ImageTransitionDelegate.shared.presentAnimationOptions = [.curveLinear] 76 | ImageTransitionDelegate.shared.dismissAnimationOptions = [.curveEaseIn] 77 | ImageTransitionDelegate.shared.pushAnimationOptions = [.curveLinear] 78 | ImageTransitionDelegate.shared.popAnimationOptions = [.curveEaseIn] 79 | ``` 80 | 81 | ## Requirements 82 | 83 | - iOS 14.0 or later 84 | 85 | ## Contributing 86 | 87 | Pull requests and stars are always welcome. 88 | 89 | For bugs and feature requests, please create an issue. 90 | 91 | 1. Fork it! 92 | 2. Create your feature branch: git checkout -b my-new-feature 93 | 3. Commit your changes: git commit -am 'Add some feature' 94 | 4. Push to the branch: git push origin my-new-feature 95 | 5. Submit a pull request :D 96 | 97 | ## Author 98 | 99 | - [@shtnkgm](https://github.com/shtnkgm) / Shota Nakagami [![Twitter](https://img.shields.io/twitter/follow/shtnkgm?style=social)](https://twitter.com/shtnkgm) 100 | 101 | ## License 102 | 103 | ImageTransition is released under the MIT license. See [LICENSE](https://github.com/shtnkgm/ImageTransition/blob/master/LICENSE) for details. 104 | -------------------------------------------------------------------------------- /Sample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Sample 4 | // 5 | // Created by Shota Nakagami on 2018/09/19. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | let window = UIWindow(frame: UIScreen.main.bounds) 18 | self.window = window 19 | window.rootViewController = UINavigationController(rootViewController: ItemListViewController()) 20 | window.makeKeyAndVisible() 21 | return true 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/apple.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "apple-food-fruit-39803.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/apple.imageset/apple-food-fruit-39803.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/Sample/Assets.xcassets/apple.imageset/apple-food-fruit-39803.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/kiwi.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "food-fresh-fruit-51312.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/kiwi.imageset/food-fresh-fruit-51312.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/Sample/Assets.xcassets/kiwi.imageset/food-fresh-fruit-51312.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/lemon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "acid-bright-citrus-1414110.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/lemon.imageset/acid-bright-citrus-1414110.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/Sample/Assets.xcassets/lemon.imageset/acid-bright-citrus-1414110.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/orange.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "citrus-food-fresh-42059.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/orange.imageset/citrus-food-fresh-42059.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/Sample/Assets.xcassets/orange.imageset/citrus-food-fresh-42059.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/strawberry.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "close-up-food-fruit-59945.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/strawberry.imageset/close-up-food-fruit-59945.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/Sample/Assets.xcassets/strawberry.imageset/close-up-food-fruit-59945.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/tomato.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cherry-tomatoes-food-freshness-48802.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/tomato.imageset/cherry-tomatoes-food-freshness-48802.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/Sample/Assets.xcassets/tomato.imageset/cherry-tomatoes-food-freshness-48802.jpg -------------------------------------------------------------------------------- /Sample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Sample/ItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCell.swift 3 | // Sample 4 | // 5 | // Created by Shota Nakagami on 2019/03/24. 6 | // Copyright © 2019 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemCell: UICollectionViewCell { 12 | static let identifier = String(describing: ItemCell.self) 13 | 14 | @IBOutlet private(set) weak var imageView: UIImageView! 15 | @IBOutlet private(set) weak var titleLabel: UILabel! 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | } 20 | 21 | func set(image: UIImage?, title: String) { 22 | imageView.image = image 23 | titleLabel.text = title 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sample/ItemCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Sample/ItemDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DestinationViewController.swift 3 | // Sample 4 | // 5 | // Created by Shota Nakagami on 2018/09/20. 6 | // Copyright © 2018年 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ImageTransition 11 | 12 | final class ItemDetailViewController: UIViewController { 13 | @IBOutlet private weak var imageView: UIImageView! 14 | @IBOutlet private weak var titleLabel: UILabel! 15 | 16 | private let dependency: Dependency 17 | 18 | struct Dependency { 19 | let image: UIImage 20 | let title: String 21 | } 22 | 23 | init(dependency: Dependency) { 24 | self.dependency = dependency 25 | super.init(nibName: nil, bundle: nil) 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | 35 | imageView.image = dependency.image 36 | titleLabel.text = dependency.title 37 | 38 | imageView.isUserInteractionEnabled = true 39 | let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewDidTapped)) 40 | imageView.addGestureRecognizer(tapGestureRecognizer) 41 | } 42 | 43 | @objc private func imageViewDidTapped() { 44 | dismiss(animated: true, completion: nil) 45 | } 46 | } 47 | 48 | extension ItemDetailViewController: ImageTransitionable { 49 | var imageViewForTransition: UIImageView? { 50 | return imageView 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sample/ItemDetailViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 | Every season's fruit is attractive to each, and when you look at the harvested fruit you just get excited just to imagine "How delicious it would be if you make this a cake!" 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Sample/ItemListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewController.swift 3 | // Sample 4 | // 5 | // Created by Shota Nakagami on 2019/03/24. 6 | // Copyright © 2019 Shota Nakagami. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ImageTransition 11 | 12 | class ItemListViewController: UIViewController { 13 | 14 | private let margin: CGFloat = 1 15 | private let column: CGFloat = 3 16 | 17 | private let items: [String] = ["kiwi", "strawberry", "apple", "orange", "tomato", "lemon"] 18 | 19 | @IBOutlet private weak var collectionView: UICollectionView! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | collectionView.register(UINib(nibName: ItemCell.identifier, bundle: nil), forCellWithReuseIdentifier: ItemCell.identifier) 24 | collectionView.delegate = self 25 | collectionView.dataSource = self 26 | collectionView.reloadData() 27 | } 28 | 29 | private func selectedCell() -> ItemCell? { 30 | guard let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first, 31 | let selectedCell = collectionView.cellForItem(at: selectedIndexPath) as? ItemCell else { 32 | assertionFailure() 33 | return nil 34 | } 35 | return selectedCell 36 | } 37 | } 38 | 39 | extension ItemListViewController: ImageTransitionable { 40 | var imageViewForTransition: UIImageView? { 41 | guard let selectedCell = selectedCell() else { return nil } 42 | return selectedCell.imageView 43 | } 44 | } 45 | 46 | extension ItemListViewController: UICollectionViewDelegate { 47 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 48 | guard let selectedCell = selectedCell(), 49 | let image = selectedCell.imageView.image, 50 | let title = selectedCell.titleLabel.text else { return } 51 | 52 | let destinationViewController = ItemDetailViewController(dependency: .init(image: image, title: title)) 53 | navigationController?.delegate = ImageTransitionDelegate.shared 54 | ImageTransitionDelegate.shared.dismissDuration = 1 55 | ImageTransitionDelegate.shared.presentDuration = 1 56 | navigationController?.pushViewController(destinationViewController, animated: true) 57 | } 58 | } 59 | 60 | extension ItemListViewController: UICollectionViewDataSource { 61 | func numberOfSections(in collectionView: UICollectionView) -> Int { 62 | return 1 63 | } 64 | 65 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 66 | return 30 67 | } 68 | 69 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 70 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ItemCell.identifier, for: indexPath) 71 | guard let itemCell = cell as? ItemCell else { fatalError() } 72 | guard let item = items.randomElement() else { fatalError() } 73 | itemCell.set(image: UIImage(named: item), title: item) 74 | return cell 75 | } 76 | } 77 | 78 | extension ItemListViewController: UICollectionViewDelegateFlowLayout { 79 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 80 | let width = (UIScreen.main.bounds.width - margin * (column + 1)) / column 81 | return CGSize(width: width, height: width * 1.5) 82 | } 83 | 84 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 85 | return margin 86 | } 87 | 88 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { 89 | return margin 90 | } 91 | 92 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 93 | return UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Sample/ItemListViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/assets/sample_01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/docs/assets/sample_01.gif -------------------------------------------------------------------------------- /docs/assets/sample_01.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/docs/assets/sample_01.mov -------------------------------------------------------------------------------- /docs/assets/sample_02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/docs/assets/sample_02.gif -------------------------------------------------------------------------------- /docs/assets/sample_02.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shtnkgm/ImageTransition/30b503ac5a5bb1bb3ba95bc418155e4985eeb164/docs/assets/sample_02.mov --------------------------------------------------------------------------------