[英]iOS tap to focus
I used this code to achieve Tap-to-Focus in iOS custom camera App, but it isn't working.我使用此代码在 iOS 自定义相机 App 中实现了 Tap-to-Focus,但它不起作用。 Here's the code
这是代码
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()
}
}
}
With a videoView: UIView
displaying the video, and cameraDevice: AVCaptureDevice
, the following seems to work for me: 使用
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()
}
}
}
Note that I had to swap the x
and y
coordinates, and remap the x
coord from 1 to 0 instead of 0 to 1 — not sure why that should be the case but it seems to be necessary to get it to work right (though it's a little tricky to test it too) . 请注意,我必须交换
x
和y
坐标,并将x
coord从1重新映射到0而不是0到1 - 不知道为什么会出现这种情况但似乎有必要让它正常工作(尽管它是有点棘手的测试它) 。
Edit: Apple's documentation explains the reason for the coordinate transformation. 编辑: Apple的文档解释了坐标转换的原因。
In addition, a device may support a focus point of interest.
另外,设备可以支持关注焦点。 You test for support using focusPointOfInterestSupported.
您使用focusPointOfInterestSupported测试支持。 If it's supported, you set the focal point using focusPointOfInterest.
如果支持,则使用focusPointOfInterest设置焦点。 You pass a CGPoint where {0,0} represents the top left of the picture area, and {1,1} represents the bottom right in landscape mode with the home button on the right—this applies even if the device is in portrait mode.
您传递一个CGPoint,其中{0,0}表示图片区域的左上角,{1,1}表示横向模式的右下角,右侧的主页按钮 - 即使设备处于纵向模式,这也适用。
In my example I had been using .ContinuousAutoFocus
and .ContinuousAutoExposure
, but the documentation indicates .AutoFocus
is the right choice. 在我的例子我已经使用了
.ContinuousAutoFocus
和.ContinuousAutoExposure
,但文件表明.AutoFocus
是正确的选择。 Oddly the documentation makes no mention of .AutoExpose
, but I'm using it in my code and it works fine. 奇怪的是文档没有提到
.AutoExpose
,但我在我的代码中使用它并且它工作正常。
I also modified my example code to include .focusPointOfInterestSupported
and .exposurePointOfInterestSupported
tests — the documentation also mentions using the isFocusModeSupported:
and isExposureModeSupported:
methods for a given focus/exposure mode to test whether it is available on a given device before setting it, but I assume if the device supports the point of interest modes then it also supports the auto modes. 我还修改了我的示例代码以包含
.focusPointOfInterestSupported
和.exposurePointOfInterestSupported
测试 - 文档还提到使用isFocusModeSupported:
和isExposureModeSupported:
给定焦点/曝光模式的方法来测试它在设置之前是否在给定设备上可用,但是我假设设备支持兴趣点模式,那么它也支持自动模式。 It all seems to work fine in my app. 这一切似乎在我的应用程序中正常工作。
Swift 3.0 Solution Swift 3.0解决方案
Converted Cody's answer into a working solution with Swift 3. 将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
I don't why this works, but it did. 我不知道为什么会这样,但确实如此。
The better way to set focus point of interest: 设置焦点的更好方法是:
first calculate the point of interest: 首先计算兴趣点:
let devicePoint: CGPoint = (self.previewView.layer as! AVCaptureVideoPreviewLayer).captureDevicePointOfInterestForPoint(gestureRecognizer.locationInView(gestureRecognizer.view))
after that set the focus point of interest : 之后设置了焦点:
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) }
You should read Apple docs on focusPointOfInterest
, which says three important things: 你应该阅读关于
focusPointOfInterest
Apple 文档 ,它说三件重要的事情:
Setting a value for this property does not initiate a focusing operation.
为此属性设置值不会启动聚焦操作。 To focus the camera on a point of interest, first set this property's value, then set the focusMode property to autoFocus or continuousAutoFocus.
要将相机聚焦在感兴趣的点上,首先设置此属性的值,然后将focusMode属性设置为autoFocus或continuousAutoFocus。
This property's CGPoint value uses a coordinate system where {0,0} is the top left of the picture area and {1,1} is the bottom right.
此属性的CGPoint值使用坐标系,其中{0,0}是图片区域的左上角,{1,1}是右下角。 This coordinate system is always relative to a landscape device orientation with the home button on the right, regardless of the actual device orientation.
无论实际的设备方向如何,此坐标系始终相对于横向设备方向,主菜单按钮位于右侧。 You can convert between this coordinate system and view coordinates using AVCaptureVideoPreviewLayer methods.
您可以使用AVCaptureVideoPreviewLayer方法在此坐标系和视图坐标之间进行转换。
Before changing the value of this property, you must call lockForConfiguration() to acquire exclusive access to the device's configuration properties.
在更改此属性的值之前,必须调用lockForConfiguration()以获取对设备配置属性的独占访问权。 Otherwise, setting the value of this property raises an exception.
否则,设置此属性的值会引发异常。 When you are done configuring the device, call unlockForConfiguration() to release the lock and allow other devices to configure the settings.
完成配置设备后,请调用unlockForConfiguration()以释放锁定并允许其他设备配置设置。
Here is an implementation which does all of that: 这是一个实现所有这些的实现:
// 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)
}
}
You have to call the methods in the right order: 您必须按正确的顺序调用方法:
if(device.lockForConfiguration(nil)) {
device.focusPointOfInterest = CGPointMake(focus_x, focus_y)
device.focusMode = AVCaptureFocusMode.ContinuousAutoFocus
device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
device.unlockForConfiguration()
}
Set the point of interest before setting the focus mode else the focus will be made on the previous point of interest. 在设置焦点模式之前设置关注点,否则焦点将在之前的关注点上进行。
The same apply for exposurePointOfInterest
. 这同样适用于
exposurePointOfInterest
。
Swift 5.0 version斯威夫特 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: 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.