如何为 AVPlayerController 的字幕添加手势?
How do I add gesture to AVPlayerController's subtitles?
请帮我解决问题..我找不到任何关于这个的问题..
我要在 AVPlayerViewController 中添加字幕。
我做到了。但是,我的客户想在字幕中添加手势。
例如,
- 点击字幕的某个关键词
- 将“点击的关键字”传递给另一个控制器
- 只需显示另一个带有点击关键字的控制器。
首先...是否可以在字幕中添加手势?
我没有很多编码经验...
我的英语不是很好..sry..TT
안녕吴成金!欢迎来到论坛。不幸的是,简短的回答是 否 。没有 api 可以在特定点击位置查询单词,甚至不会使用 UILabel
(或 CATextLayer
)呈现内容。
较长的答案是:如果您真的想这样做,有多种选择,但我不会将其用于生产。在显示字幕时检查 AVPlayerViewController
的视图层次结构会告诉您字幕是在 FigFCRCALayerOutputNodeLayer
中呈现的。在模拟器上 运行 iOS 14.4 这一层位于
avPlayerVc.view.subviews.first?.subviews.first?.subviews.first?.layer.sublayers?.first?.sublayers?[1].sublayers?.first?.sublayers?.first?.sublayers?.first
(这可能会在未来的任何时候改变,甚至可能在这个版本以下都不起作用)。文字直接设置为图层content
(包括圆角半透明背景视图)
我玩了一会儿 Vision,虽然它有点滞后,但它可以将图层内容 (CGImage) 提供给文本识别请求,然后将结果拆分为单词并检查触摸位置是否在范围内词的界限。为了获得触摸点,我将 AVPlayerViewController
子类化(我知道这很糟糕,但如果直接使用 AVPlayerLayer
会更容易)并将触摸从 touchesBegan
转换为屏幕坐标:
final class PlayerVC: AVPlayerViewController {
var onTap: ((CGPoint) -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let tapLocation = touches.first?.location(in: nil) else { return }
onTap?(tapLocation)
}
}
在该位置获取单词的实际代码如下所示(playerVc
指向一个 PlayerVC
实例:
func tap(tapLocation: CGPoint) {
guard
let subtitleLayer = playerVc.view.subviews.first?.subviews.first?.subviews.first?.layer.sublayers?.first?.sublayers?[1].sublayers?.first?.sublayers?.first?.sublayers?.first,
CFGetTypeID(subtitleLayer.contents as CFTypeRef) == CGImage.typeID
else { return }
let image = subtitleLayer.contents as! CGImage
let requestHandler = VNImageRequestHandler(cgImage: image)
let recognizeTextHandler: (VNRequest, Error?) -> Void = { request, error in
guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
for observation in observations {
guard let topCandidate = observation.topCandidates(1).first, topCandidate.string != "" else { continue }
for word in topCandidate.string.components(separatedBy: " ") {
guard let range = topCandidate.string.range(of: word) else { continue }
if let boundinBox = try? topCandidate.boundingBox(for: range) {
let transform = CGAffineTransform.identity
.scaledBy(x: 1, y: -1)
.translatedBy(x: 0, y: -subtitleLayer.frame.size.height)
.scaledBy(x: subtitleLayer.frame.size.width, y: subtitleLayer.frame.size.height)
let convertedTopLeft = boundinBox.topLeft.applying(transform)
let convertedBottomRight = boundinBox.bottomRight.applying(transform)
let localRect = CGRect(x: convertedTopLeft.x,
y: convertedTopLeft.y,
width: convertedBottomRight.x - convertedTopLeft.x,
height: convertedBottomRight.y - convertedTopLeft.y)
let globalRect = subtitleLayer.convert(localRect, to: nil)
if globalRect.contains(tapLocation) {
print("You tapped \(word)")
}
}
}
}
}
let request = VNRecognizeTextRequest(completionHandler: recognizeTextHandler)
request.usesLanguageCorrection = false
request.recognitionLevel = .accurate
do {
try requestHandler.perform([request])
} catch {
print("Unable to perform the request \(error)")
}
}
请帮我解决问题..我找不到任何关于这个的问题..
我要在 AVPlayerViewController 中添加字幕。
我做到了。但是,我的客户想在字幕中添加手势。
例如,
- 点击字幕的某个关键词
- 将“点击的关键字”传递给另一个控制器
- 只需显示另一个带有点击关键字的控制器。
首先...是否可以在字幕中添加手势?
我没有很多编码经验...
我的英语不是很好..sry..TT
안녕吴成金!欢迎来到论坛。不幸的是,简短的回答是 否 。没有 api 可以在特定点击位置查询单词,甚至不会使用 UILabel
(或 CATextLayer
)呈现内容。
较长的答案是:如果您真的想这样做,有多种选择,但我不会将其用于生产。在显示字幕时检查 AVPlayerViewController
的视图层次结构会告诉您字幕是在 FigFCRCALayerOutputNodeLayer
中呈现的。在模拟器上 运行 iOS 14.4 这一层位于
avPlayerVc.view.subviews.first?.subviews.first?.subviews.first?.layer.sublayers?.first?.sublayers?[1].sublayers?.first?.sublayers?.first?.sublayers?.first
(这可能会在未来的任何时候改变,甚至可能在这个版本以下都不起作用)。文字直接设置为图层content
(包括圆角半透明背景视图)
我玩了一会儿 Vision,虽然它有点滞后,但它可以将图层内容 (CGImage) 提供给文本识别请求,然后将结果拆分为单词并检查触摸位置是否在范围内词的界限。为了获得触摸点,我将 AVPlayerViewController
子类化(我知道这很糟糕,但如果直接使用 AVPlayerLayer
会更容易)并将触摸从 touchesBegan
转换为屏幕坐标:
final class PlayerVC: AVPlayerViewController {
var onTap: ((CGPoint) -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let tapLocation = touches.first?.location(in: nil) else { return }
onTap?(tapLocation)
}
}
在该位置获取单词的实际代码如下所示(playerVc
指向一个 PlayerVC
实例:
func tap(tapLocation: CGPoint) {
guard
let subtitleLayer = playerVc.view.subviews.first?.subviews.first?.subviews.first?.layer.sublayers?.first?.sublayers?[1].sublayers?.first?.sublayers?.first?.sublayers?.first,
CFGetTypeID(subtitleLayer.contents as CFTypeRef) == CGImage.typeID
else { return }
let image = subtitleLayer.contents as! CGImage
let requestHandler = VNImageRequestHandler(cgImage: image)
let recognizeTextHandler: (VNRequest, Error?) -> Void = { request, error in
guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
for observation in observations {
guard let topCandidate = observation.topCandidates(1).first, topCandidate.string != "" else { continue }
for word in topCandidate.string.components(separatedBy: " ") {
guard let range = topCandidate.string.range(of: word) else { continue }
if let boundinBox = try? topCandidate.boundingBox(for: range) {
let transform = CGAffineTransform.identity
.scaledBy(x: 1, y: -1)
.translatedBy(x: 0, y: -subtitleLayer.frame.size.height)
.scaledBy(x: subtitleLayer.frame.size.width, y: subtitleLayer.frame.size.height)
let convertedTopLeft = boundinBox.topLeft.applying(transform)
let convertedBottomRight = boundinBox.bottomRight.applying(transform)
let localRect = CGRect(x: convertedTopLeft.x,
y: convertedTopLeft.y,
width: convertedBottomRight.x - convertedTopLeft.x,
height: convertedBottomRight.y - convertedTopLeft.y)
let globalRect = subtitleLayer.convert(localRect, to: nil)
if globalRect.contains(tapLocation) {
print("You tapped \(word)")
}
}
}
}
}
let request = VNRecognizeTextRequest(completionHandler: recognizeTextHandler)
request.usesLanguageCorrection = false
request.recognitionLevel = .accurate
do {
try requestHandler.perform([request])
} catch {
print("Unable to perform the request \(error)")
}
}