[英]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.