diff --git a/imglyKit.podspec b/imglyKit.podspec index 06c0d36a..e975db29 100644 --- a/imglyKit.podspec +++ b/imglyKit.podspec @@ -14,5 +14,5 @@ Pod::Spec.new do |s| s.source_files = ['imglyKit/Camera/**/*', 'imglyKit/Editor/**/*', 'imglyKit/Extensions/**/*', 'imglyKit/Processing/**/*', 'imglyKit/*.swift'] s.resources = ['imglyKit/Assets.xcassets', 'imglyKit/Filter Responses/*.png', 'imglyKit/Fonts/*', 'imglyKit/en.lproj/Localizable.strings'] - s.frameworks = 'Accelerate', 'AVFoundation', 'CoreImage', 'Foundation', 'GLKit', 'MobileCoreServices', 'OpenGLES', 'Photos', 'UIKit' + s.frameworks = 'Accelerate', 'AVFoundation', 'CoreImage', 'CoreMotion', 'Foundation', 'GLKit', 'MobileCoreServices', 'OpenGLES', 'Photos', 'UIKit' end diff --git a/imglyKit.xcodeproj/project.pbxproj b/imglyKit.xcodeproj/project.pbxproj index 056f1bb2..7fed89a6 100644 --- a/imglyKit.xcodeproj/project.pbxproj +++ b/imglyKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 2BEBCE971B00D7D1000FE22B /* IMGLYAnimationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BEBCE961B00D7D1000FE22B /* IMGLYAnimationDelegate.swift */; }; 2BF4D38A1AF1139600C801B2 /* imglyKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BF4D3891AF1139600C801B2 /* imglyKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2BF4D3A81AF114F000C801B2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2BF4D3A71AF114F000C801B2 /* Assets.xcassets */; }; 2BF4D3AB1AF114FA00C801B2 /* IMGLYCameraController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BF4D3A91AF114FA00C801B2 /* IMGLYCameraController.swift */; }; @@ -218,6 +219,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 2BEBCE961B00D7D1000FE22B /* IMGLYAnimationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IMGLYAnimationDelegate.swift; sourceTree = ""; }; 2BF4D3841AF1139600C801B2 /* imglyKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = imglyKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2BF4D3881AF1139600C801B2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2BF4D3891AF1139600C801B2 /* imglyKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = imglyKit.h; sourceTree = ""; }; @@ -487,6 +489,7 @@ 2BF4D3A01AF113E600C801B2 /* Camera */ = { isa = PBXGroup; children = ( + 2BEBCE961B00D7D1000FE22B /* IMGLYAnimationDelegate.swift */, 2BF4D3A91AF114FA00C801B2 /* IMGLYCameraController.swift */, 2BF4D3AA1AF114FA00C801B2 /* IMGLYCameraViewController.swift */, ); @@ -906,6 +909,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2BEBCE971B00D7D1000FE22B /* IMGLYAnimationDelegate.swift in Sources */, 2BF4D3DA1AF1150500C801B2 /* IMGLYFontSelectorView.swift in Sources */, 2BF4D3E31AF1150500C801B2 /* IMGLYSliderEditorViewController.swift in Sources */, 2BF4D3DF1AF1150500C801B2 /* IMGLYNavigationAnimationController.swift in Sources */, diff --git a/imglyKit/Camera/IMGLYAnimationDelegate.swift b/imglyKit/Camera/IMGLYAnimationDelegate.swift new file mode 100644 index 00000000..1ae5a954 --- /dev/null +++ b/imglyKit/Camera/IMGLYAnimationDelegate.swift @@ -0,0 +1,30 @@ +// +// IMGLYAnimationDelegate.swift +// imglyKit +// +// Created by Sascha Schwabbauer on 11/05/15. +// Copyright (c) 2015 9elements GmbH. All rights reserved. +// + +import QuartzCore + +public typealias IMGLYAnimationDelegateBlock = (Bool) -> (Void) + +public class IMGLYAnimationDelegate: NSObject { + + // MARK: - Properties + + public let block: IMGLYAnimationDelegateBlock + + // MARK: - Initializers + + init(block: IMGLYAnimationDelegateBlock) { + self.block = block + } + + // MARK: - Animation Delegate + + public override func animationDidStop(anim: CAAnimation!, finished flag: Bool) { + block(flag) + } +} diff --git a/imglyKit/Camera/IMGLYCameraController.swift b/imglyKit/Camera/IMGLYCameraController.swift index 4ef9864c..71ef307a 100644 --- a/imglyKit/Camera/IMGLYCameraController.swift +++ b/imglyKit/Camera/IMGLYCameraController.swift @@ -10,8 +10,10 @@ import UIKit import OpenGLES import GLKit import AVFoundation +import CoreMotion private let kIMGLYIndicatorSize = CGFloat(75) +private let kIMGLYDeviceMotionThreshold = Double(0.4) public protocol IMGLYCameraControllerDelegate: class { func captureSessionStarted() @@ -28,7 +30,6 @@ It provides methods to start a capture session, toggle between cameras, or selec public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate { public weak var delegate: IMGLYCameraControllerDelegate? public let tapGestureRecognizer = UITapGestureRecognizer() - public let doubleTapGestureRecognizer = UITapGestureRecognizer() // MARK:- private vars private var glContext_ : EAGLContext! @@ -60,6 +61,10 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff //private var flashMode_ = AVCaptureFlashMode.Auto private var flashModeIndex_ = 0 private var supportedFlashModes_:[AVCaptureFlashMode] = [] + + private lazy var motionManager = CMMotionManager() + private lazy var motionManagerQueue = NSOperationQueue() + private var initialAttitude: CMAttitude? // MARK: - computed vars @@ -133,12 +138,7 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff focusIndicatorLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5) previewView!.layer.addSublayer(focusIndicatorLayer) - doubleTapGestureRecognizer.addTarget(self, action: "doubleTapped:") - doubleTapGestureRecognizer.numberOfTapsRequired = 2 - videoPreviewView.addGestureRecognizer(doubleTapGestureRecognizer) - tapGestureRecognizer.addTarget(self, action: "tapped:") - tapGestureRecognizer.requireGestureRecognizerToFail(doubleTapGestureRecognizer) videoPreviewView.addGestureRecognizer(tapGestureRecognizer) } @@ -459,8 +459,66 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff videoPreviewView.display() } + // MARK: - Focus Lock + + public func disableFocusLockAnimated(animated: Bool) { + motionManager.stopDeviceMotionUpdates() + initialAttitude = nil + + if isFocusPointSupported || isExposurePointSupported { + resetFocusAndExposurePoints() + + if animated { + CATransaction.begin() + CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) + focusIndicatorLayer.borderColor = UIColor.whiteColor().CGColor + focusIndicatorLayer.frame = CGRect(x: 0, y: 0, width: kIMGLYIndicatorSize * 2, height: kIMGLYIndicatorSize * 2) + focusIndicatorLayer.transform = CATransform3DIdentity + + if let previewView = previewView { + focusIndicatorLayer.position = previewView.center + } + + CATransaction.commit() + + let resizeAnimation = CABasicAnimation(keyPath: "transform") + resizeAnimation.duration = 0.25 + resizeAnimation.fromValue = NSValue(CATransform3D: CATransform3DMakeScale(1.5, 1.5, 1)) + resizeAnimation.delegate = IMGLYAnimationDelegate(block: { _ in + let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.85 * Double(NSEC_PER_SEC))) + dispatch_after(delayTime, dispatch_get_main_queue()) { + self.focusIndicatorLayer.opacity = 0 + + let fadeAnimation = CABasicAnimation(keyPath: "opacity") + fadeAnimation.duration = 0.25 + fadeAnimation.fromValue = 1 + fadeAnimation.delegate = IMGLYAnimationDelegate(block: { _ in + CATransaction.begin() + CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) + self.focusIndicatorLayer.hidden = true + self.focusIndicatorLayer.opacity = 1 + self.focusIndicatorLayer.frame = CGRect(x: 0, y: 0, width: kIMGLYIndicatorSize, height: kIMGLYIndicatorSize) + CATransaction.commit() + }) + + self.focusIndicatorLayer.addAnimation(fadeAnimation, forKey: nil) + } + }) + + focusIndicatorLayer.addAnimation(resizeAnimation, forKey: nil) + } else { + focusIndicatorLayer.hidden = true + } + } + } + // MARK: - Helpers + // get magnitude of vector via Pythagorean theorem + private func magnitudeFromAttitude(attitude: CMAttitude) -> Double { + return sqrt(pow(attitude.roll, 2) + pow(attitude.yaw, 2) + pow(attitude.pitch, 2)) + } + private func fitRect(sourceRect: CGRect, intoTargetRect targetRect: CGRect, withContentMode contentMode: UIViewContentMode) -> CGRect { if !(contentMode == .ScaleAspectFit || contentMode == .ScaleAspectFill) { // Not implemented @@ -526,6 +584,22 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff if focusIndicatorLayer.hidden == false { if videoDevice.focusMode == .Locked && videoDevice.exposureMode == .Locked { focusIndicatorLayer.borderColor = UIColor(white: 1, alpha: 0.5).CGColor + + // Check MotionManager for rotation and disable focus when movement has been detected + motionManager.startDeviceMotionUpdatesToQueue(motionManagerQueue) { [unowned self] (deviceMotion, error) in + if let initialAttitude = self.initialAttitude { + deviceMotion.attitude.multiplyByInverseOfAttitude(initialAttitude) + let magnitude = self.magnitudeFromAttitude(deviceMotion.attitude) ?? 0 + + if magnitude > kIMGLYDeviceMotionThreshold { + NSOperationQueue.mainQueue().addOperationWithBlock { + self.disableFocusLockAnimated(true) + } + } + } else { + self.initialAttitude = deviceMotion.attitude + } + } } } } @@ -568,6 +642,7 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff private func showFocusIndicatorLayerAtLocation(location: CGPoint) { CATransaction.begin() CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) + focusIndicatorLayer.opacity = 1 focusIndicatorLayer.hidden = false focusIndicatorLayer.borderColor = UIColor.whiteColor().CGColor focusIndicatorLayer.position = location @@ -600,11 +675,4 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff } } } - - @objc private func doubleTapped(recognizer: UITapGestureRecognizer) { - if isFocusPointSupported || isExposurePointSupported { - focusIndicatorLayer.hidden = true - resetFocusAndExposurePoints() - } - } }