[英]iOS tap to focus
我使用此代码在 iOS 自定义相机 App 中实现了 Tap-to-Focus,但它不起作用。 这是代码
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touchPer = touches.anyObject() as UITouch
let screenSize = UIScreen.mainScreen().bounds.size
var focus_x = touchPer.locationInView(self.view).x / screenSize.width
var focus_y = touchPer.locationInView(self.view).y / screenSize.height
if let device = captureDevice {
if(device.lockForConfiguration(nil)) {
device.focusMode = AVCaptureFocusMode.ContinuousAutoFocus
device.focusPointOfInterest = CGPointMake(focus_x, focus_y)
device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
device.unlockForConfiguration()
}
}
}
使用videoView: UIView
显示视频和cameraDevice: AVCaptureDevice
,以下似乎对我cameraDevice: AVCaptureDevice
:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touchPoint = touches.first as! UITouch
var screenSize = videoView.bounds.size
var focusPoint = CGPoint(x: touchPoint.locationInView(videoView).y / screenSize.height, y: 1.0 - touchPoint.locationInView(videoView.x / screenSize.width)
if let device = cameraDevice {
if(device.lockForConfiguration(nil)) {
if device.focusPointOfInterestSupported {
device.focusPointOfInterest = focusPoint
device.focusMode = AVCaptureFocusMode.AutoFocus
}
if device.exposurePointOfInterestSupported {
device.exposurePointOfInterest = focusPoint
device.exposureMode = AVCaptureExposureMode.AutoExpose
}
device.unlockForConfiguration()
}
}
}
请注意,我必须交换x
和y
坐标,并将x
coord从1重新映射到0而不是0到1 - 不知道为什么会出现这种情况但似乎有必要让它正常工作(尽管它是有点棘手的测试它) 。
编辑: Apple的文档解释了坐标转换的原因。
另外,设备可以支持关注焦点。 您使用focusPointOfInterestSupported测试支持。 如果支持,则使用focusPointOfInterest设置焦点。 您传递一个CGPoint,其中{0,0}表示图片区域的左上角,{1,1}表示横向模式的右下角,右侧的主页按钮 - 即使设备处于纵向模式,这也适用。
在我的例子我已经使用了.ContinuousAutoFocus
和.ContinuousAutoExposure
,但文件表明.AutoFocus
是正确的选择。 奇怪的是文档没有提到.AutoExpose
,但我在我的代码中使用它并且它工作正常。
我还修改了我的示例代码以包含.focusPointOfInterestSupported
和.exposurePointOfInterestSupported
测试 - 文档还提到使用isFocusModeSupported:
和isExposureModeSupported:
给定焦点/曝光模式的方法来测试它在设置之前是否在给定设备上可用,但是我假设设备支持兴趣点模式,那么它也支持自动模式。 这一切似乎在我的应用程序中正常工作。
Swift 3.0解决方案
将Cody的答案转换为Swift 3的工作解决方案。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchPoint = touches.first! as UITouch
let screenSize = cameraView.bounds.size
let focusPoint = CGPoint(x: touchPoint.location(in: cameraView).y / screenSize.height, y: 1.0 - touchPoint.location(in: cameraView).x / screenSize.width)
if let device = captureDevice {
do {
try device.lockForConfiguration()
if device.isFocusPointOfInterestSupported {
device.focusPointOfInterest = focusPoint
device.focusMode = AVCaptureFocusMode.autoFocus
}
if device.isExposurePointOfInterestSupported {
device.exposurePointOfInterest = focusPoint
device.exposureMode = AVCaptureExposureMode.autoExpose
}
device.unlockForConfiguration()
} catch {
// Handle errors here
}
}
}
device.focusPointOfInterest = focusPoint
device.focusMode = AVCaptureFocusMode.AutoFocus
device.exposurePointOfInterest = focusPoint
device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
我不知道为什么会这样,但确实如此。
设置焦点的更好方法是:
首先计算兴趣点:
let devicePoint: CGPoint = (self.previewView.layer as! AVCaptureVideoPreviewLayer).captureDevicePointOfInterestForPoint(gestureRecognizer.locationInView(gestureRecognizer.view))
之后设置了焦点:
let device: AVCaptureDevice! = self.videoDeviceInput!.device do { try device.lockForConfiguration() if device.focusPointOfInterestSupported && device.isFocusModeSupported(focusMode){ device.focusPointOfInterest = devicePoint device.focusMode = focusMode } device.unlockForConfiguration() }catch{ print(error) }
你应该阅读关于focusPointOfInterest
Apple 文档 ,它说三件重要的事情:
为此属性设置值不会启动聚焦操作。 要将相机聚焦在感兴趣的点上,首先设置此属性的值,然后将focusMode属性设置为autoFocus或continuousAutoFocus。
此属性的CGPoint值使用坐标系,其中{0,0}是图片区域的左上角,{1,1}是右下角。 无论实际的设备方向如何,此坐标系始终相对于横向设备方向,主菜单按钮位于右侧。 您可以使用AVCaptureVideoPreviewLayer方法在此坐标系和视图坐标之间进行转换。
在更改此属性的值之前,必须调用lockForConfiguration()以获取对设备配置属性的独占访问权。 否则,设置此属性的值会引发异常。 完成配置设备后,请调用unlockForConfiguration()以释放锁定并允许其他设备配置设置。
这是一个实现所有这些的实现:
// In your camera preview view
@objc private func cameraViewTapped(with gestureRecognizer: UITapGestureRecognizer) {
let location = gestureRecognizer.location(in: self)
addFocusIndicatorView(at: location) // If you want to indicate it in the UI
// This is the point you want to pass to your capture device
let captureDeviceLocation = previewLayer.captureDevicePointConverted(fromLayerPoint: location)
// Somehow pass the point to where your AVCaptureDevice is
viewDelegate?.cameraPreviewView(self, didTapToFocusAt: captureDeviceLocation)
}
// In your camera controller
func focus(at point: CGPoint) {
guard let device = videoDevice else {
return
}
guard device.isFocusPointOfInterestSupported, device.isExposurePointOfInterestSupported else {
return
}
do {
try device.lockForConfiguration()
device.focusPointOfInterest = point
device.exposurePointOfInterest = point
device.focusMode = .continuousAutoFocus
device.exposureMode = .continuousAutoExposure
device.unlockForConfiguration()
} catch {
print(error)
}
}
您必须按正确的顺序调用方法:
if(device.lockForConfiguration(nil)) {
device.focusPointOfInterest = CGPointMake(focus_x, focus_y)
device.focusMode = AVCaptureFocusMode.ContinuousAutoFocus
device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
device.unlockForConfiguration()
}
在设置焦点模式之前设置关注点,否则焦点将在之前的关注点上进行。
这同样适用于exposurePointOfInterest
。
斯威夫特 5.0 版本
// The back camera as default device
var captureDevice: AVCaptureDevice? {
return AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
}
// The camera view.
var cameraView: UIView!
// The layer that contains the camera output
var previewLayer: AVCaptureVideoPreviewLayer
// The focus square view - the yellow one ;)
var squareFocusView: UIView
// User taps on screen to select focus
@IBAction func tapToFocus(_ sender: UITapGestureRecognizer) {
// make sure we capture one tap only
if (sender.state == .ended) {
guard let captureDevice = captureDevice else {
return
}
let tappedFocusPoint = sender.location(in: cameraView)
// we need to move the focus point to be the center of the tap instead of (0.0, 0.0)
let centerX = tappedFocusPoint.x - (squareFocusView.frame.size.width / 2.0)
let centerY = tappedFocusPoint.y - (squareFocusView.frame.size.height / 2.0)
let focusPoint = CGPoint(x: centerX, y: centerY)
// we need to remap the point because of different coordination systems.
let convertedFocusPoint = previewLayer.captureDevicePointConverted(fromLayerPoint: focusPoint)
do {
// changing focusMode and exposureMode requires the device config to be locked.
try captureDevice.lockForConfiguration()
if (captureDevice.isFocusModeSupported(.autoFocus) && captureDevice.isFocusPointOfInterestSupported) {
captureDevice.focusPointOfInterest = convertedFocusPoint
captureDevice.focusMode = .autoFocus
}
if (captureDevice.isExposureModeSupported(.autoExpose) && captureDevice.isExposurePointOfInterestSupported) {
captureDevice.exposurePointOfInterest = convertedFocusPoint
captureDevice.exposureMode = .autoExpose
}
// unlocks device config
captureDevice.unlockForConfiguration()
} catch {
// handle error here
}
}
}
Swift 4:
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
var touchPoint = touches.first as! UITouch
let cameraView = cameraViewController.view
var screenSize = cameraView!.bounds.size
var focusPoint = CGPoint(x: touchPoint.location(in: cameraView).y / screenSize.height, y: 1.0 - touchPoint.location(in: cameraView).x / screenSize.width)
if #available(iOS 10.0, *) {
let device = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: .unspecified)
do{
try device?.lockForConfiguration()
if device!.isFocusPointOfInterestSupported {
device!.focusPointOfInterest = focusPoint
device!.focusMode = AVCaptureDevice.FocusMode.autoFocus
}
if device!.isExposurePointOfInterestSupported {
device!.exposurePointOfInterest = focusPoint
device!.exposureMode = AVCaptureDevice.ExposureMode.autoExpose
}
device!.unlockForConfiguration()
}catch{
}
} else {
// Fallback on earlier versions
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.