├── .gitignore ├── Classes ├── ImageMaskAnimator.swift ├── ImageMaskTransition.swift └── ImageMaskUtils.swift ├── ImageMaskTransition.podspec ├── ImageMaskTransition.xcodeproj └── project.pbxproj ├── ImageMaskTransition ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DetailViewController.swift ├── Info.plist ├── ViewController.swift ├── movie.jpg └── topImage.jpg ├── LICENSE ├── README.md └── Screenshot ├── demo.gif └── logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | # For Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | # If you use cocoa pod 20 | Pods -------------------------------------------------------------------------------- /Classes/ImageMaskAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePresentAnimator.swift 3 | // ImageMaskTransition 4 | // 5 | // Created by huangwenchen on 16/8/17. 6 | // Copyright © 2016年 Leo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | enum ImageMaskTransitionType{ 13 | case Present 14 | case Dismisss 15 | } 16 | class ImageMaskAnimator: NSObject,UIViewControllerAnimatedTransitioning { 17 | var config:TransitionConfig 18 | var maskContentView:UIImageView! 19 | var imageView:UIImageView! 20 | var blurImage:UIImage? 21 | var transitionType:ImageMaskTransitionType = .Present 22 | init(config:TransitionConfig) { 23 | self.config = config 24 | super.init() 25 | } 26 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 27 | let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! 28 | let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! 29 | let containView = transitionContext.containerView 30 | let frame = UIScreen.main.bounds 31 | maskContentView = UIImageView(frame: frame) 32 | maskContentView.backgroundColor = UIColor.lightGray 33 | 34 | if self.transitionType == .Present { 35 | let fromImageView = self.config.fromImageView 36 | fromImageView.isHidden = true 37 | let adjustFromRect = fromImageView.convert(fromImageView.bounds, to: containView) 38 | 39 | let toImageView = self.config.toImageView! 40 | toImageView.isHidden = true 41 | //Create Content Blur View 42 | #if (arch(i386) || arch(x86_64)) && os(iOS) 43 | print("Wow,CIFilter is too slow on simulator,So I disable blur on Simulator") 44 | #else 45 | self.blurImage = fromView.blurScreenShot(3.0) 46 | maskContentView.image = fromView.blurScreenShot(3.0) 47 | #endif 48 | maskContentView.frame = containView.bounds 49 | containView.addSubview(self.maskContentView) 50 | 51 | 52 | let adjustToRect = toImageView.convert(toImageView.bounds, to: containView) 53 | 54 | imageView = UIImageView(frame: adjustFromRect) 55 | imageView.image = fromImageView.image 56 | containView.addSubview(imageView) 57 | 58 | //Set up shadow 59 | imageView.layer.shadowColor = UIColor.black.cgColor 60 | imageView.layer.shadowOffset = CGSize(width: 2.0, height: 2.0) 61 | imageView.layer.shadowRadius = 10.0 62 | imageView.layer.shadowOpacity = 0.8 63 | 64 | //Animation phase 1,change transform and frame 65 | UIView.animate(withDuration: 0.5 / 1.6 * self.config.presentDuration, animations: { 66 | self.imageView.frame = adjustToRect 67 | self.imageView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) 68 | }) { (finished) in 69 | //Animation phase 2,change transform to default,clear shadow 70 | UIView.animate(withDuration: 0.3 / 1.6 * self.config.presentDuration, animations: { 71 | self.imageView.transform = CGAffineTransform.identity 72 | self.imageView.layer.shadowOpacity = 0.0 73 | }) { (finished) in 74 | //Animation phase 3,start mask animation 75 | containView.addSubview(toView) 76 | containView.bringSubview(toFront: self.imageView) 77 | let adjustFrame = self.imageView.convert(self.imageView.bounds, to: self.maskContentView) 78 | toView.maskFrom(adjustFrame, duration: 0.8 / 1.6 * self.config.presentDuration,complete: { 79 | self.maskContentView.removeFromSuperview() 80 | self.imageView.removeFromSuperview() 81 | self.maskContentView = nil 82 | self.imageView = nil 83 | transitionContext.completeTransition(true) 84 | toImageView.isHidden = false 85 | fromImageView.isHidden = false 86 | }) 87 | } 88 | } 89 | }else{ 90 | #if (arch(i386) || arch(x86_64)) && os(iOS) 91 | print("Wow,CIFilter is too slow on simulator,So I disable blur on Simulator") 92 | #else 93 | maskContentView.image = self.blurImage 94 | #endif 95 | maskContentView.frame = containView.bounds 96 | containView.addSubview(self.maskContentView) 97 | 98 | let fromImageView = self.config.fromImageView 99 | let toImageView = self.config.toImageView! 100 | fromImageView.isHidden = true 101 | toImageView.isHidden = true 102 | let adjustFromRect = fromImageView.convert(fromImageView.bounds, to: containView) 103 | let adjustToRect = toImageView.convert(toImageView.bounds, to: containView) 104 | imageView = UIImageView(frame:adjustToRect) 105 | imageView.image = fromImageView.image 106 | containView.addSubview(imageView) 107 | 108 | //Animation phase 1,animate mask 109 | containView.bringSubview(toFront: self.imageView) 110 | containView.sendSubview(toBack: maskContentView) 111 | let adjustFrame = self.imageView.convert(self.imageView.bounds, to: self.maskContentView) 112 | fromView.maskTo(adjustFrame, duration: 0.8 / 1.3 * self.config.dismissDuration,complete: { 113 | //Set up shadow 114 | self.imageView.layer.shadowColor = UIColor.black.cgColor 115 | self.imageView.layer.shadowOffset = CGSize(width: 2.0, height: 2.0) 116 | self.imageView.layer.shadowRadius = 10.0 117 | self.imageView.layer.shadowOpacity = 0.8 118 | UIView.animate(withDuration: 0.5 / 1.3 * self.config.dismissDuration, animations: { 119 | self.imageView.frame = adjustFromRect 120 | }) { (finished) in 121 | self.maskContentView.removeFromSuperview() 122 | self.imageView.removeFromSuperview() 123 | self.maskContentView = nil 124 | self.imageView = nil 125 | self.config.toImageView = nil 126 | containView.addSubview(toView) 127 | transitionContext.completeTransition(true) 128 | toImageView.isHidden = false 129 | fromImageView.isHidden = false 130 | //Animation phase 2,change transform to default,clear shadow 131 | } 132 | 133 | }) 134 | } 135 | 136 | } 137 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 138 | return self.transitionType == .Present ? self.config.presentDuration:self.config.dismissDuration 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Classes/ImageMaskTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageMaskTransition.swift 3 | // ImageMaskTransition 4 | // 5 | // Created by huangwenchen on 16/8/17. 6 | // Copyright © 2016年 Leo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct TransitionConfig{ 12 | public var fromImageView:UIImageView 13 | public var toImageView:UIImageView? 14 | public var blurRadius:CGFloat = 3.0 15 | public var presentDuration = 1.6 16 | public var dismissDuration = 1.3 17 | public static func defaultConfig(fromImageView:UIImageView,toImageView:UIImageView)->TransitionConfig{ 18 | return TransitionConfig(fromImageView: fromImageView, toImageView: toImageView, blurRadius: 3.0, presentDuration: 1.6, dismissDuration: 1.3) 19 | } 20 | } 21 | open class ImageMaskTransition: NSObject,UINavigationControllerDelegate,UIViewControllerTransitioningDelegate{ 22 | open let config:TransitionConfig 23 | let animator:ImageMaskAnimator 24 | public init(config:TransitionConfig){ 25 | self.config = config 26 | self.animator = ImageMaskAnimator(config: config) 27 | super.init() 28 | } 29 | // MARK: - UINavigationControllerDelegate - 30 | open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { 31 | switch operation { 32 | case .pop: 33 | self.animator.transitionType = ImageMaskTransitionType.Dismisss 34 | return self.animator 35 | case .push: 36 | self.animator.transitionType = ImageMaskTransitionType.Present 37 | return self.animator 38 | default: 39 | return nil 40 | } 41 | } 42 | // MARK: - UIViewControllerTransitioningDelegate - 43 | open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 44 | self.animator.transitionType = ImageMaskTransitionType.Present 45 | return self.animator 46 | } 47 | open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 48 | self.animator.transitionType = ImageMaskTransitionType.Dismisss 49 | return self.animator 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Classes/ImageMaskUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // ImageMaskTransition 4 | // 5 | // Created by huangwenchen on 16/8/17. 6 | // Copyright © 2016年 Leo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIView{ 13 | func maskFrom(_ fromRect:CGRect,duration:TimeInterval ,complete:@escaping ()->() = {}){ 14 | CATransaction.begin() 15 | CATransaction.setCompletionBlock { 16 | complete() 17 | } 18 | let maskLayer = CAShapeLayer() 19 | let fromCenter = CGPoint(x: fromRect.origin.x + fromRect.size.width / 2.0, y: fromRect.origin.y + fromRect.size.height / 2) 20 | let fromRadius = min(fromRect.width/2,fromRect.height/2) 21 | let fromPath = UIBezierPath(arcCenter: fromCenter, radius: fromRadius, startAngle: 0, endAngle: CGFloat(M_PI) * 2, clockwise: true) 22 | 23 | let viewWidth = self.frame.size.width 24 | let viewHeight = self.frame.size.height 25 | 26 | let r1 = sqrt(fromCenter.x * fromCenter.x + fromCenter.y * fromCenter.y) 27 | let r2 = sqrt((fromCenter.x - viewWidth) * (fromCenter.x - viewWidth) + fromCenter.y * fromCenter.y) 28 | let r32 = (fromCenter.x - viewWidth) * (fromCenter.x - viewWidth) + (fromCenter.y - viewHeight) * (fromCenter.y - viewHeight) 29 | let r3 = sqrt(r32) 30 | let r4 = sqrt(fromCenter.x * fromCenter.x + (fromCenter.y - viewHeight) * (fromCenter.y - viewHeight)) 31 | let toRadius = max(max(max(r1,r2),r3),r4) 32 | 33 | let toPath = UIBezierPath(arcCenter: fromCenter, radius: toRadius, startAngle: 0, endAngle: CGFloat(M_PI) * 2, clockwise: true) 34 | 35 | maskLayer.path = toPath.cgPath 36 | let basicAnimation = CABasicAnimation(keyPath: "path") 37 | basicAnimation.duration = duration 38 | basicAnimation.fromValue = fromPath.cgPath 39 | basicAnimation.toValue = toPath.cgPath 40 | basicAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 41 | 42 | maskLayer.add(basicAnimation, forKey: "pathMask") 43 | self.layer.mask = maskLayer 44 | CATransaction.commit() 45 | } 46 | func maskTo(_ toRect:CGRect,duration:TimeInterval ,complete:@escaping ()->() = {}){ 47 | CATransaction.begin() 48 | CATransaction.setCompletionBlock { 49 | complete() 50 | } 51 | let maskLayer = CAShapeLayer() 52 | let toCenter = CGPoint(x: toRect.origin.x + toRect.size.width / 2.0, y: toRect.origin.y + toRect.size.height / 2) 53 | let toRadius:CGFloat = 0.001 54 | let toPath = UIBezierPath(arcCenter: toCenter, radius: toRadius, startAngle: 0, endAngle: CGFloat(M_PI) * 2, clockwise: true) 55 | 56 | let viewWidth = self.frame.size.width 57 | let viewHeight = self.frame.size.height 58 | 59 | let r1 = sqrt(toCenter.x * toCenter.x + toCenter.y * toCenter.y) 60 | let r2 = sqrt((toCenter.x - viewWidth) * (toCenter.x - viewWidth) + toCenter.y * toCenter.y) 61 | let r32 = (toCenter.x - viewWidth) * (toCenter.x - viewWidth) + (toCenter.y - viewHeight) * (toCenter.y - viewHeight) 62 | let r3 = sqrt(r32) 63 | let r4 = sqrt(toCenter.x * toCenter.x + (toCenter.y - viewHeight) * (toCenter.y - viewHeight)) 64 | let fromRadius = max(max(max(r1,r2),r3),r4) 65 | 66 | let fromPath = UIBezierPath(arcCenter: toCenter, radius: fromRadius, startAngle: 0, endAngle: CGFloat(M_PI) * 2, clockwise: true) 67 | 68 | maskLayer.path = toPath.cgPath 69 | let basicAnimation = CABasicAnimation(keyPath: "path") 70 | basicAnimation.duration = duration 71 | basicAnimation.fromValue = fromPath.cgPath 72 | basicAnimation.toValue = toPath.cgPath 73 | basicAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 74 | 75 | maskLayer.add(basicAnimation, forKey: "pathMask") 76 | self.layer.mask = maskLayer 77 | CATransaction.commit() 78 | } 79 | func blurScreenShot(_ blurRadius:CGFloat)->UIImage?{ 80 | guard self.superview != nil else{ 81 | return nil 82 | } 83 | UIGraphicsBeginImageContextWithOptions(CGSize(width: frame.width, height: frame.height), false, 1) 84 | layer.render(in: UIGraphicsGetCurrentContext()!) 85 | let image = UIGraphicsGetImageFromCurrentImageContext() 86 | UIGraphicsEndImageContext(); 87 | guard let blur = CIFilter(name: "CIGaussianBlur") else{ 88 | return nil 89 | } 90 | blur.setValue(CIImage(image: image!), forKey: kCIInputImageKey) 91 | blur.setValue(blurRadius, forKey: kCIInputRadiusKey) 92 | let ciContext = CIContext(options: nil) 93 | let result = blur.value(forKey: kCIOutputImageKey) as! CIImage! 94 | let boundingRect = CGRect(x:0, 95 | y: 0, 96 | width: frame.width, 97 | height: frame.height) 98 | 99 | let cgImage = ciContext.createCGImage(result!, from: boundingRect) 100 | let filteredImage = UIImage(cgImage: cgImage!) 101 | return filteredImage 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /ImageMaskTransition.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = 'ImageMaskTransition' 4 | s.version = '0.2.0' 5 | s.summary = 'Simple but powerful and beautiful ViewController transtion for Swift 3' 6 | s.description = <<-DESC 7 | This is a ViewController transition to create beautiful image transition between ViewControllers 8 | DESC 9 | 10 | s.homepage = 'https://github.com/LeoMobileDeveloper/ImageMaskTransition' 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { 'Leo' => 'leomobiledeveloper@gmail.com' } 13 | s.source = { :git => 'https://github.com/LeoMobileDeveloper/ImageMaskTransition.git', :tag => s.version.to_s } 14 | s.ios.deployment_target = '8.0' 15 | s.source_files = 'Classes/**/*' 16 | end 17 | -------------------------------------------------------------------------------- /ImageMaskTransition.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52127A731D645A54007B64A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52127A721D645A54007B64A2 /* AppDelegate.swift */; }; 11 | 52127A751D645A54007B64A2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52127A741D645A54007B64A2 /* ViewController.swift */; }; 12 | 52127A781D645A54007B64A2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52127A761D645A54007B64A2 /* Main.storyboard */; }; 13 | 52127A7A1D645A54007B64A2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52127A791D645A54007B64A2 /* Assets.xcassets */; }; 14 | 52127A7D1D645A54007B64A2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52127A7B1D645A54007B64A2 /* LaunchScreen.storyboard */; }; 15 | 5250031D1D6808780051CE92 /* topImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5250031C1D6808780051CE92 /* topImage.jpg */; }; 16 | 52AFCC311D66F90D00CBED16 /* ImageMaskAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AFCC2E1D66F90D00CBED16 /* ImageMaskAnimator.swift */; }; 17 | 52AFCC321D66F90D00CBED16 /* ImageMaskTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AFCC2F1D66F90D00CBED16 /* ImageMaskTransition.swift */; }; 18 | 52AFCC331D66F90D00CBED16 /* ImageMaskUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AFCC301D66F90D00CBED16 /* ImageMaskUtils.swift */; }; 19 | 52AFCC351D66F91A00CBED16 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AFCC341D66F91A00CBED16 /* DetailViewController.swift */; }; 20 | 52AFCC371D66F93F00CBED16 /* movie.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 52AFCC361D66F93F00CBED16 /* movie.jpg */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 52127A6F1D645A54007B64A2 /* ImageMaskTransition.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageMaskTransition.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 52127A721D645A54007B64A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | 52127A741D645A54007B64A2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | 52127A771D645A54007B64A2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 52127A791D645A54007B64A2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 52127A7C1D645A54007B64A2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 52127A7E1D645A54007B64A2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 5250031C1D6808780051CE92 /* topImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = topImage.jpg; sourceTree = ""; }; 32 | 52AFCC2E1D66F90D00CBED16 /* ImageMaskAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageMaskAnimator.swift; path = Classes/ImageMaskAnimator.swift; sourceTree = SOURCE_ROOT; }; 33 | 52AFCC2F1D66F90D00CBED16 /* ImageMaskTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageMaskTransition.swift; path = Classes/ImageMaskTransition.swift; sourceTree = SOURCE_ROOT; }; 34 | 52AFCC301D66F90D00CBED16 /* ImageMaskUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageMaskUtils.swift; path = Classes/ImageMaskUtils.swift; sourceTree = SOURCE_ROOT; }; 35 | 52AFCC341D66F91A00CBED16 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 36 | 52AFCC361D66F93F00CBED16 /* movie.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = movie.jpg; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 52127A6C1D645A54007B64A2 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 52127A661D645A54007B64A2 = { 51 | isa = PBXGroup; 52 | children = ( 53 | 52127A711D645A54007B64A2 /* ImageMaskTransition */, 54 | 52127A701D645A54007B64A2 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 52127A701D645A54007B64A2 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 52127A6F1D645A54007B64A2 /* ImageMaskTransition.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 52127A711D645A54007B64A2 /* ImageMaskTransition */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 52127A841D645A6D007B64A2 /* Classes */, 70 | 52127A721D645A54007B64A2 /* AppDelegate.swift */, 71 | 52AFCC361D66F93F00CBED16 /* movie.jpg */, 72 | 5250031C1D6808780051CE92 /* topImage.jpg */, 73 | 52AFCC341D66F91A00CBED16 /* DetailViewController.swift */, 74 | 52127A741D645A54007B64A2 /* ViewController.swift */, 75 | 52127A761D645A54007B64A2 /* Main.storyboard */, 76 | 52127A791D645A54007B64A2 /* Assets.xcassets */, 77 | 52127A7B1D645A54007B64A2 /* LaunchScreen.storyboard */, 78 | 52127A7E1D645A54007B64A2 /* Info.plist */, 79 | ); 80 | path = ImageMaskTransition; 81 | sourceTree = ""; 82 | }; 83 | 52127A841D645A6D007B64A2 /* Classes */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 52AFCC2E1D66F90D00CBED16 /* ImageMaskAnimator.swift */, 87 | 52AFCC2F1D66F90D00CBED16 /* ImageMaskTransition.swift */, 88 | 52AFCC301D66F90D00CBED16 /* ImageMaskUtils.swift */, 89 | ); 90 | path = Classes; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | 52127A6E1D645A54007B64A2 /* ImageMaskTransition */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = 52127A811D645A54007B64A2 /* Build configuration list for PBXNativeTarget "ImageMaskTransition" */; 99 | buildPhases = ( 100 | 52127A6B1D645A54007B64A2 /* Sources */, 101 | 52127A6C1D645A54007B64A2 /* Frameworks */, 102 | 52127A6D1D645A54007B64A2 /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = ImageMaskTransition; 109 | productName = ImageMaskTransition; 110 | productReference = 52127A6F1D645A54007B64A2 /* ImageMaskTransition.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 52127A671D645A54007B64A2 /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastSwiftUpdateCheck = 0730; 120 | LastUpgradeCheck = 0800; 121 | ORGANIZATIONNAME = Leo; 122 | TargetAttributes = { 123 | 52127A6E1D645A54007B64A2 = { 124 | CreatedOnToolsVersion = 7.3.1; 125 | DevelopmentTeam = BE3S9DV86J; 126 | LastSwiftMigration = 0800; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 52127A6A1D645A54007B64A2 /* Build configuration list for PBXProject "ImageMaskTransition" */; 131 | compatibilityVersion = "Xcode 3.2"; 132 | developmentRegion = English; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 52127A661D645A54007B64A2; 139 | productRefGroup = 52127A701D645A54007B64A2 /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 52127A6E1D645A54007B64A2 /* ImageMaskTransition */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 52127A6D1D645A54007B64A2 /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 52AFCC371D66F93F00CBED16 /* movie.jpg in Resources */, 154 | 52127A7D1D645A54007B64A2 /* LaunchScreen.storyboard in Resources */, 155 | 5250031D1D6808780051CE92 /* topImage.jpg in Resources */, 156 | 52127A7A1D645A54007B64A2 /* Assets.xcassets in Resources */, 157 | 52127A781D645A54007B64A2 /* Main.storyboard in Resources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXResourcesBuildPhase section */ 162 | 163 | /* Begin PBXSourcesBuildPhase section */ 164 | 52127A6B1D645A54007B64A2 /* Sources */ = { 165 | isa = PBXSourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | 52127A751D645A54007B64A2 /* ViewController.swift in Sources */, 169 | 52AFCC331D66F90D00CBED16 /* ImageMaskUtils.swift in Sources */, 170 | 52AFCC311D66F90D00CBED16 /* ImageMaskAnimator.swift in Sources */, 171 | 52AFCC351D66F91A00CBED16 /* DetailViewController.swift in Sources */, 172 | 52AFCC321D66F90D00CBED16 /* ImageMaskTransition.swift in Sources */, 173 | 52127A731D645A54007B64A2 /* AppDelegate.swift in Sources */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXSourcesBuildPhase section */ 178 | 179 | /* Begin PBXVariantGroup section */ 180 | 52127A761D645A54007B64A2 /* Main.storyboard */ = { 181 | isa = PBXVariantGroup; 182 | children = ( 183 | 52127A771D645A54007B64A2 /* Base */, 184 | ); 185 | name = Main.storyboard; 186 | sourceTree = ""; 187 | }; 188 | 52127A7B1D645A54007B64A2 /* LaunchScreen.storyboard */ = { 189 | isa = PBXVariantGroup; 190 | children = ( 191 | 52127A7C1D645A54007B64A2 /* Base */, 192 | ); 193 | name = LaunchScreen.storyboard; 194 | sourceTree = ""; 195 | }; 196 | /* End PBXVariantGroup section */ 197 | 198 | /* Begin XCBuildConfiguration section */ 199 | 52127A7F1D645A54007B64A2 /* Debug */ = { 200 | isa = XCBuildConfiguration; 201 | buildSettings = { 202 | ALWAYS_SEARCH_USER_PATHS = NO; 203 | CLANG_ANALYZER_NONNULL = YES; 204 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 205 | CLANG_CXX_LIBRARY = "libc++"; 206 | CLANG_ENABLE_MODULES = YES; 207 | CLANG_ENABLE_OBJC_ARC = YES; 208 | CLANG_WARN_BOOL_CONVERSION = YES; 209 | CLANG_WARN_CONSTANT_CONVERSION = YES; 210 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 211 | CLANG_WARN_EMPTY_BODY = YES; 212 | CLANG_WARN_ENUM_CONVERSION = YES; 213 | CLANG_WARN_INFINITE_RECURSION = YES; 214 | CLANG_WARN_INT_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 217 | CLANG_WARN_UNREACHABLE_CODE = YES; 218 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 219 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 220 | COPY_PHASE_STRIP = NO; 221 | DEBUG_INFORMATION_FORMAT = dwarf; 222 | ENABLE_STRICT_OBJC_MSGSEND = YES; 223 | ENABLE_TESTABILITY = YES; 224 | GCC_C_LANGUAGE_STANDARD = gnu99; 225 | GCC_DYNAMIC_NO_PIC = NO; 226 | GCC_NO_COMMON_BLOCKS = YES; 227 | GCC_OPTIMIZATION_LEVEL = 0; 228 | GCC_PREPROCESSOR_DEFINITIONS = ( 229 | "DEBUG=1", 230 | "$(inherited)", 231 | ); 232 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 233 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 234 | GCC_WARN_UNDECLARED_SELECTOR = YES; 235 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 236 | GCC_WARN_UNUSED_FUNCTION = YES; 237 | GCC_WARN_UNUSED_VARIABLE = YES; 238 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 239 | MTL_ENABLE_DEBUG_INFO = YES; 240 | ONLY_ACTIVE_ARCH = YES; 241 | SDKROOT = iphoneos; 242 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 243 | TARGETED_DEVICE_FAMILY = "1,2"; 244 | }; 245 | name = Debug; 246 | }; 247 | 52127A801D645A54007B64A2 /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | CLANG_ANALYZER_NONNULL = YES; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BOOL_CONVERSION = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INFINITE_RECURSION = YES; 262 | CLANG_WARN_INT_CONVERSION = YES; 263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 264 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 265 | CLANG_WARN_UNREACHABLE_CODE = YES; 266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 267 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 268 | COPY_PHASE_STRIP = NO; 269 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 270 | ENABLE_NS_ASSERTIONS = NO; 271 | ENABLE_STRICT_OBJC_MSGSEND = YES; 272 | GCC_C_LANGUAGE_STANDARD = gnu99; 273 | GCC_NO_COMMON_BLOCKS = YES; 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 281 | MTL_ENABLE_DEBUG_INFO = NO; 282 | SDKROOT = iphoneos; 283 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 284 | TARGETED_DEVICE_FAMILY = "1,2"; 285 | VALIDATE_PRODUCT = YES; 286 | }; 287 | name = Release; 288 | }; 289 | 52127A821D645A54007B64A2 /* Debug */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 293 | DEVELOPMENT_TEAM = BE3S9DV86J; 294 | INFOPLIST_FILE = ImageMaskTransition/Info.plist; 295 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 296 | PRODUCT_BUNDLE_IDENTIFIER = Leo.ImageMaskTransition; 297 | PRODUCT_NAME = "$(TARGET_NAME)"; 298 | SWIFT_VERSION = 3.0; 299 | }; 300 | name = Debug; 301 | }; 302 | 52127A831D645A54007B64A2 /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 306 | DEVELOPMENT_TEAM = BE3S9DV86J; 307 | INFOPLIST_FILE = ImageMaskTransition/Info.plist; 308 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 309 | PRODUCT_BUNDLE_IDENTIFIER = Leo.ImageMaskTransition; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | SWIFT_VERSION = 3.0; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | 52127A6A1D645A54007B64A2 /* Build configuration list for PBXProject "ImageMaskTransition" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | 52127A7F1D645A54007B64A2 /* Debug */, 322 | 52127A801D645A54007B64A2 /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | 52127A811D645A54007B64A2 /* Build configuration list for PBXNativeTarget "ImageMaskTransition" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | 52127A821D645A54007B64A2 /* Debug */, 331 | 52127A831D645A54007B64A2 /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | /* End XCConfigurationList section */ 337 | }; 338 | rootObject = 52127A671D645A54007B64A2 /* Project object */; 339 | } 340 | -------------------------------------------------------------------------------- /ImageMaskTransition/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ImageMaskTransition 4 | // 5 | // Created by huangwenchen on 16/8/17. 6 | // Copyright © 2016年 Leo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ImageMaskTransition/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ImageMaskTransition/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ImageMaskTransition/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ImageMaskTransition/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // ImageMaskTransition 4 | // 5 | // Created by huangwenchen on 16/8/18. 6 | // Copyright © 2016年 Leo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DetailViewController: UIViewController { 12 | let imageView = UIImageView() 13 | let button = UIButton(type: .custom) 14 | let scrollView = UIScrollView() 15 | var topView = UIImageView() 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | view.addSubview(scrollView) 19 | view.backgroundColor = UIColor.white 20 | scrollView.frame = self.view.bounds 21 | scrollView.contentSize = CGSize(width: self.view.bounds.size.width , height: self.view.bounds.size.height * 2) 22 | 23 | topView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width,height: 200) 24 | topView.image = UIImage(named: "topImage.jpg") 25 | 26 | scrollView.addSubview(topView) 27 | 28 | imageView.image = UIImage(named: "movie.jpg") 29 | imageView.frame = CGRect(x: 0, y: 0, width: 80, height: 124) 30 | imageView.center = CGPoint(x: 60, y: 200) 31 | scrollView.addSubview(imageView) 32 | 33 | button.setTitle("Back", for: UIControlState()) 34 | button.setTitleColor(UIColor.white, for: UIControlState()) 35 | button.sizeToFit() 36 | button.center = CGPoint(x: self.view.frame.width - 30, y: 25) 37 | view.addSubview(button) 38 | button.addTarget(self, action: #selector(DetailViewController.dismissController), for: UIControlEvents.touchUpInside) 39 | // Do any additional setup after loading the view. 40 | } 41 | func dismissController(){ 42 | if self.navigationController != nil { 43 | _ = self.navigationController?.popViewController(animated: true) 44 | }else{ 45 | self.dismiss(animated: true, completion: nil) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ImageMaskTransition/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ImageMaskTransition/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ImageMaskTransition 4 | // 5 | // Created by huangwenchen on 16/8/17. 6 | // Copyright © 2016年 Leo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CollectionCell:UICollectionViewCell{ 12 | let imageView = UIImageView() 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | contentView.addSubview(imageView) 16 | } 17 | required init?(coder aDecoder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | override func layoutSubviews() { 21 | super.layoutSubviews() 22 | imageView.frame = contentView.bounds 23 | } 24 | } 25 | 26 | class ViewController: UICollectionViewController { 27 | //Hold a reference here 28 | var imageMaskTransiton:ImageMaskTransition? 29 | 30 | var useModalPresent = true 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | self.collectionView?.register(CollectionCell.self, forCellWithReuseIdentifier: "cell") 34 | self.collectionView?.backgroundColor = UIColor.white 35 | } 36 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 37 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CollectionCell 38 | cell?.imageView.image = UIImage(named: "movie.jpg") 39 | return cell! 40 | } 41 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 42 | return 40 43 | } 44 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 45 | collectionView.deselectItem(at: indexPath, animated: true) 46 | let cell = collectionView.cellForItem(at: indexPath) as! CollectionCell 47 | 48 | //Create a detail controller,the frame of toImageView in viewDidLoad should be the same after layoutsubviews 49 | let dvc = DetailViewController() 50 | let config = TransitionConfig.defaultConfig(fromImageView: cell.imageView, toImageView:dvc.imageView) 51 | imageMaskTransiton = ImageMaskTransition(config: config) 52 | if useModalPresent{ 53 | dvc.transitioningDelegate = imageMaskTransiton 54 | present(dvc, animated: true, completion: nil) 55 | }else{ 56 | self.navigationController?.delegate = imageMaskTransiton 57 | self.navigationController?.pushViewController(dvc, animated: true) 58 | } 59 | 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /ImageMaskTransition/movie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoMobileDeveloper/ImageMaskTransition/366cd0e6af087fbb3c6606ac4f8e8b15aa28e9ba/ImageMaskTransition/movie.jpg -------------------------------------------------------------------------------- /ImageMaskTransition/topImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoMobileDeveloper/ImageMaskTransition/366cd0e6af087fbb3c6606ac4f8e8b15aa28e9ba/ImageMaskTransition/topImage.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Leo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 5 | 6 |

7 | 8 | 9 | [![Version](https://img.shields.io/cocoapods/v/ImageMaskTransition.svg?style=flat)](http://cocoapods.org/pods/ImageMaskTransition) [![Platform](http://img.shields.io/badge/platform-ios-blue.svg?style=flat 10 | )](https://developer.apple.com/iphone/index.action) 11 | [![Language](https://img.shields.io/badge/language-swift%203.0-brightgreen.svg?style=flat 12 | )](https://developer.apple.com/swift) 13 | [![License](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat 14 | )](http://mit-license.org) 15 | 16 | Present then Dismiss 17 | 18 |

19 | 20 | 21 | 22 |

23 | 24 | ## Require 25 | 26 | - iOS 8 27 | - Swift 3.0 28 | 29 | !!!!!You need to run the Demo Project on device to see blur effect 30 | 31 | For Swift 2.3,See branch [Swift2](https://github.com/LeoMobileDeveloper/ImageMaskTransition/tree/Swift2) 32 | 33 | ## Install 34 | 35 | CocoaPod 36 | 37 | ``` 38 | pod "ImageMaskTransition" 39 | ``` 40 | 41 | 42 | ## Useage 43 | 44 | Hold a property of `ImageMaskTransition` in firstViewController 45 | 46 | ``` 47 | var imageMaskTransiton:ImageMaskTransition? 48 | ``` 49 | 50 | Present a View Controller 51 | 52 | The frame of `toImageView` must be the final frame after layout 53 | 54 | ``` 55 | let dvc = DetailViewController() 56 | let config = TransitionConfig.defaultConfig(fromImageView: cell.imageView, toImageView:dvc.imageView) 57 | imageMaskTransiton = ImageMaskTransition(config: config) 58 | dvc.transitioningDelegate = imageMaskTransiton 59 | present(dvc, animated: true, completion: nil) 60 | ``` 61 | 62 | Push a ViewController 63 | 64 | ``` 65 | let dvc = DetailViewController() 66 | let config = TransitionConfig.defaultConfig(fromImageView: cell.imageView, toImageView:dvc.imageView) 67 | imageMaskTransiton = ImageMaskTransition(config: config) 68 | self.navigationController?.delegate = imageMaskTransiton 69 | self.navigationController?.pushViewController(dvc, animated: true) 70 | ``` 71 | 72 | 73 | ## Author 74 | 75 | Leo, leomobiledeveloper@gmail.com 76 | 77 | ## License 78 | 79 | ImageMaskTransition is available under the MIT license. See the LICENSE file for more info. 80 | 81 | ## 中文 82 | 关于实现原理,参见这片博客 83 | 84 | [实现一个复杂动画的界面转场(Swift)](http://blog.csdn.net/hello_hwc/article/details/52265854) 85 | 86 | -------------------------------------------------------------------------------- /Screenshot/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoMobileDeveloper/ImageMaskTransition/366cd0e6af087fbb3c6606ac4f8e8b15aa28e9ba/Screenshot/demo.gif -------------------------------------------------------------------------------- /Screenshot/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoMobileDeveloper/ImageMaskTransition/366cd0e6af087fbb3c6606ac4f8e8b15aa28e9ba/Screenshot/logo.png --------------------------------------------------------------------------------