Skip to content

Commit

Permalink
Updated Focus Lock to mimic Camera app
Browse files Browse the repository at this point in the history
  • Loading branch information
sascha committed May 11, 2015
1 parent ddbc689 commit b788688
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 14 deletions.
2 changes: 1 addition & 1 deletion imglyKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions imglyKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -218,6 +219,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
2BEBCE961B00D7D1000FE22B /* IMGLYAnimationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IMGLYAnimationDelegate.swift; sourceTree = "<group>"; };
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 = "<group>"; };
2BF4D3891AF1139600C801B2 /* imglyKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = imglyKit.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -487,6 +489,7 @@
2BF4D3A01AF113E600C801B2 /* Camera */ = {
isa = PBXGroup;
children = (
2BEBCE961B00D7D1000FE22B /* IMGLYAnimationDelegate.swift */,
2BF4D3A91AF114FA00C801B2 /* IMGLYCameraController.swift */,
2BF4D3AA1AF114FA00C801B2 /* IMGLYCameraViewController.swift */,
);
Expand Down Expand Up @@ -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 */,
Expand Down
30 changes: 30 additions & 0 deletions imglyKit/Camera/IMGLYAnimationDelegate.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
94 changes: 81 additions & 13 deletions imglyKit/Camera/IMGLYCameraController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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!
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
}
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -600,11 +675,4 @@ public class IMGLYCameraController: NSObject, AVCaptureVideoDataOutputSampleBuff
}
}
}

@objc private func doubleTapped(recognizer: UITapGestureRecognizer) {
if isFocusPointSupported || isExposurePointSupported {
focusIndicatorLayer.hidden = true
resetFocusAndExposurePoints()
}
}
}

0 comments on commit b788688

Please sign in to comment.