| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- import UIKit
- @objcMembers
- public final class LNVAPWrapView: UIView {
- private var playerView: LNVAPPlayerView?
- private var delegateBridge = LNWrapDelegateBridge()
- public weak var delegate: LNVAPWrapPlaybackDelegate?
- public weak var legacyDelegate: LNVAPWrapLegacyPlaybackDelegate?
- public var contentModeOption: LNVAPWrapContentMode = .scaleToFill {
- didSet {
- applyContentModeIfPossible()
- }
- }
- @objc(lnWrapContentMode)
- public var lnWrapContentMode: Int {
- get { contentModeOption.legacyRawValue }
- set { contentModeOption = LNVAPWrapContentMode(rawValue: newValue) ?? .scaleToFill }
- }
- public var autoDestroyAfterFinish: Bool = true
- @objc(autoDestoryAfterFinish)
- public var autoDestoryAfterFinish: Bool {
- get { autoDestroyAfterFinish }
- set { autoDestroyAfterFinish = newValue }
- }
- public override init(frame: CGRect) {
- super.init(frame: frame)
- delegateBridge.owner = self
- }
- public required init?(coder: NSCoder) {
- super.init(coder: coder)
- delegateBridge.owner = self
- }
- public override func layoutSubviews() {
- super.layoutSubviews()
- if contentModeOption == .scaleToFill {
- playerView?.frame = bounds
- }
- }
- @objc(lnPlayWithFilePath:repeatCount:)
- public func lnPlay(filePath: String, repeatCount: Int) {
- delegateBridge.lastPlayedFrameIndex = 0
- let player = initPlayerViewIfNeed()
- player.lnPlay(filePath: filePath, repeatCount: repeatCount)
- }
- @objc(lnPlayWithFilePath:)
- public func lnPlay(filePath: String) {
- lnPlay(filePath: filePath, repeatCount: 0)
- }
- @objc(lnPlayDeprecatedWithFilePath:fps:blendMode:repeatCount:delegate:)
- public func lnPlayDeprecated(filePath: String,
- fps: Int,
- blendMode: Int,
- repeatCount: Int,
- delegate: LNVAPWrapPlaybackDelegate?) {
- _ = blendMode
- self.delegate = delegate
- delegateBridge.lastPlayedFrameIndex = 0
- let player = initPlayerViewIfNeed()
- player.fps = fps
- player.lnPlay(filePath: filePath, repeatCount: repeatCount)
- }
- @objc(lnStop)
- public func lnStop() {
- playerView?.lnStop()
- }
- @objc(lnPause)
- public func lnPause() {
- playerView?.lnPause()
- }
- @objc(lnResume)
- public func lnResume() {
- playerView?.lnResume()
- }
- @objc(lnSetMute:)
- public func lnSetMute(_ mute: Bool) {
- let player = initPlayerViewIfNeed()
- player.lnSetMute(mute)
- }
- @objc(lnAddVapTapGesture:)
- public func lnAddVapTapGesture(_ handler: @escaping LNVAPGestureEventBlock) {
- let player = initPlayerViewIfNeed()
- player.lnAddVapTapGesture(handler)
- }
- @objc(lnAddVapGesture:callback:)
- public func lnAddVapGesture(_ gestureRecognizer: UIGestureRecognizer, callback: @escaping LNVAPGestureEventBlock) {
- let player = initPlayerViewIfNeed()
- player.lnAddVapGesture(gestureRecognizer, callback: callback)
- }
- @objc(addVapTapGesture:)
- public func addVapTapGesture(_ handler: @escaping LNVAPGestureEventBlock) {
- lnAddVapTapGesture(handler)
- }
- @objc(addVapGesture:callback:)
- public func addVapGesture(_ gestureRecognizer: UIGestureRecognizer, callback: @escaping LNVAPGestureEventBlock) {
- lnAddVapGesture(gestureRecognizer, callback: callback)
- }
- public override func hitTest(_ hitPoint: CGPoint, with event: UIEvent?) -> UIView? {
- if !isUserInteractionEnabled || isHidden || alpha < 0.01 {
- return nil
- }
- if point(inside: hitPoint, with: event) {
- for subview in subviews.reversed() {
- let convertedPoint = convert(hitPoint, to: subview)
- if let hitView = subview.hitTest(convertedPoint, with: event) {
- return hitView
- }
- }
- return nil
- }
- return nil
- }
- fileprivate func shouldStart(with config: LNVAPConfigModel) -> Bool {
- applyContentMode(with: config)
- if let allow = delegate?.lnWrapViewShouldStart?(self, config: config) {
- return allow
- }
- return legacyDelegate?.vapWrap_viewshouldStartPlayMP4?(self, config: config) ?? true
- }
- fileprivate func notifyStart() {
- delegate?.lnWrapViewDidStart?(self)
- legacyDelegate?.vapWrap_viewDidStartPlayMP4?(self)
- }
- fileprivate func notifyPlay(_ frame: LNMP4AnimatedImageFrame) {
- delegate?.lnWrapViewDidPlay?(self, frame: frame)
- legacyDelegate?.vapWrap_viewDidPlayMP4AtFrame?(frame, view: self)
- }
- fileprivate func notifyFinish(_ totalFrameCount: Int) {
- let computedCount = max(totalFrameCount, delegateBridge.lastPlayedFrameIndex + 1)
- delegate?.lnWrapViewDidFinish?(self, totalFrameCount: computedCount)
- legacyDelegate?.vapWrap_viewDidFinishPlayMP4?(computedCount, view: self)
- }
- fileprivate func notifyStop() {
- delegate?.lnWrapViewDidStop?(self)
- let lastFrameIndex = delegateBridge.lastPlayedFrameIndex
- legacyDelegate?.vapWrap_viewDidStopPlayMP4?(lastFrameIndex, view: self)
- delegateBridge.lastPlayedFrameIndex = 0
- DispatchQueue.main.async { [weak self] in
- guard let self else { return }
- if self.autoDestroyAfterFinish {
- self.playerView?.removeFromSuperview()
- self.playerView = nil
- }
- }
- }
- fileprivate func notifyFail(_ error: NSError) {
- delegate?.lnWrapViewDidFail?(self, error: error)
- legacyDelegate?.vapWrap_viewDidFailPlayMP4?(error)
- }
- fileprivate func contentForTag(_ tag: String, resource: LNVAPSourceInfo) -> String? {
- if let text = delegate?.lnWrapViewContent?(forTag: tag, resource: resource) {
- return text
- }
- return legacyDelegate?.vapWrapview_contentForVapTag?(tag, resource: resource)
- }
- fileprivate func loadImage(withURL url: String, context: NSDictionary, completion: @escaping LNVAPImageCompletion) {
- if let handler = delegate?.lnWrapViewLoadImage {
- handler(url, context, completion)
- return
- }
- if (legacyDelegate as AnyObject?)?.responds(to: #selector(LNVAPWrapLegacyPlaybackDelegate.vapWrapView_loadVapImage(withURL:context:completion:))) == true {
- legacyDelegate?.vapWrapView_loadVapImage?(withURL: url, context: context, completion: completion)
- return
- }
- completion(nil, nil, url)
- }
- private func initPlayerViewIfNeed() -> LNVAPPlayerView {
- if let playerView {
- return playerView
- }
- let view = LNVAPPlayerView(frame: bounds)
- view.delegate = delegateBridge
- addSubview(view)
- playerView = view
- applyContentModeIfPossible()
- return view
- }
- private func applyContentModeIfPossible() {
- guard let playerView else { return }
- guard let config = delegateBridge.lastConfig else {
- playerView.frame = bounds
- return
- }
- applyContentMode(with: config)
- }
- private func applyContentMode(with config: LNVAPConfigModel) {
- guard let playerView else { return }
- guard let info = config.info, info.size.width > 0, info.size.height > 0 else {
- playerView.frame = bounds
- return
- }
- let layoutWidth = bounds.width
- let layoutHeight = bounds.height
- guard layoutWidth > 0, layoutHeight > 0 else { return }
- let layoutRatio = layoutWidth / layoutHeight
- let videoRatio = info.size.width / info.size.height
- switch contentModeOption {
- case .scaleToFill:
- playerView.frame = bounds
- case .aspectFit:
- let realSize: CGSize
- if layoutRatio < videoRatio {
- let width = layoutWidth
- realSize = CGSize(width: width, height: width / videoRatio)
- } else {
- let height = layoutHeight
- realSize = CGSize(width: height * videoRatio, height: height)
- }
- playerView.frame = CGRect(origin: .zero, size: realSize)
- playerView.center = CGPoint(x: bounds.midX, y: bounds.midY)
- case .aspectFill:
- let realSize: CGSize
- if layoutRatio > videoRatio {
- let width = layoutWidth
- realSize = CGSize(width: width, height: width / videoRatio)
- } else {
- let height = layoutHeight
- realSize = CGSize(width: height * videoRatio, height: height)
- }
- playerView.frame = CGRect(origin: .zero, size: realSize)
- playerView.center = CGPoint(x: bounds.midX, y: bounds.midY)
- }
- }
- }
- private final class LNWrapDelegateBridge: NSObject, LNVAPPlaybackDelegate {
- weak var owner: LNVAPWrapView?
- var lastConfig: LNVAPConfigModel?
- var lastPlayedFrameIndex: Int = 0
- func lnPlayerShouldStart(_ playerView: LNVAPPlayerView, config: LNVAPConfigModel) -> Bool {
- lastConfig = config
- return owner?.shouldStart(with: config) ?? true
- }
- func lnPlayerDidStart(_ playerView: LNVAPPlayerView) {
- owner?.notifyStart()
- }
- func lnPlayerDidPlay(_ playerView: LNVAPPlayerView, frame: LNMP4AnimatedImageFrame) {
- lastPlayedFrameIndex = frame.frameIndex
- owner?.notifyPlay(frame)
- }
- func lnPlayerDidStop(_ playerView: LNVAPPlayerView) {
- owner?.notifyStop()
- }
- func lnPlayerDidFinish(_ playerView: LNVAPPlayerView, totalFrameCount: Int) {
- owner?.notifyFinish(totalFrameCount)
- }
- func lnPlayerDidFail(_ playerView: LNVAPPlayerView, error: NSError) {
- owner?.notifyFail(error)
- }
- func lnPlayerContent(forTag tag: String, resource: LNVAPSourceInfo) -> String? {
- owner?.contentForTag(tag, resource: resource)
- }
- func lnPlayerLoadImage(withURL url: String, context: NSDictionary, completion: @escaping LNVAPImageCompletion) {
- owner?.loadImage(withURL: url, context: context, completion: completion)
- }
- }
|