You need to enable JavaScript to run this app.
导航
SDK交付清单
最近更新时间:2025.04.09 20:28:12首次发布时间:2025.04.08 17:46:51
我的收藏
有用
有用
无用
无用
EffectOneSDK标准版的内容清单

1. EffectOneKit.xcframework

  • 用于自行部署或者本地导入

  • 支持真机arm64、模拟器x86架构和模拟器arm64架构

  • 内含真机arm64对应的符号表dSYMs,用于解析崩溃栈

  • 内含Privacyinfo.xcprivacy用于苹果合规要求


2. EOLocalResources.bundle

  • SDK依赖的所有资源,包括UI组件依赖的显示资源和UI组件依赖的素材资源

  • BuiltInResource目录下存放的是UI组件依赖的显示资源,包括icon、文案等

  • EffectResource目录下存放的是UI组件依赖的素材资源,详情参考EffectOne iOS 功能详解中资源导入与配置部分的介绍

  • License目录下存放的是离线的授权证书


3. EffectOneKit.podspec

4. EffectOneKit_spm

5. EffectOne-iOS

  • 基于swift语言的示例代码

  • EffectOneModule.swift 演示了SDK各个功能接口的调用

  • EOCustomRequestBuilderImpl.swift 演示了从服务端获取资源接口的定义

  • EOExportUI 是视频导出功能的实现代码

  • CutSame 是剪同款UI跟高光成片UI实现代码

EOCustomRequestBuilderImpl.swift 源码

import UIKit
import EffectOneKit


class EOCustomRequestBuilderImpl: NSObject, EOResourceRequestBuilderProtocol {
    //  {zh} 获取config  {en} Get config
    func buildConfigURLRequest(byPanelKey panelKey: String?, resourceInfo resource: [AnyHashable : Any]?) -> URLRequest? {
        let url = "https://cvsdk.tos-cn-beijing.volces.com/resource/eo/1.3.0/EffectResource/Panel_configs/\(panelKey ?? "").json"
        var request = URLRequest(url: URL(string: url)!)
        request.httpMethod = "GET"
        return request
    }
    
    //  {zh} 获取icon  {en} Get icon
    func iconUrlString(_ iconName: String?, panelKey: String?) -> String? {
        return panelKey == EOPanelKeyCutSame ? "https://cvsdk.tos-cn-beijing.volces.com/resource/eo/1.3.0/EffectResource/Resource_icons/\(iconName ?? "")" : ""
    }
    
    //  {zh} 获取视频  {en} Get video
    func videoUrlString(_ videoName: String?, panelKey: String?) -> String? {
        return "https://cvsdk.tos-cn-beijing.volces.com/resource/eo/1.3.0/EffectResource/Resource_videos/\(videoName ?? "")"
    }
    
    //  {zh} 获取模板  {en} Get template
    func buildResourceDownloadRequest(byRelativePath relativePath: String?, isModel: Bool, panelKey: String?, resourceInfo resource: [AnyHashable : Any]?) -> URLRequest? {
        let url = "https://cvsdk.tos-cn-beijing.volces.com/resource/eo/1.3.0/RemoteResource\(relativePath ?? "")"
        return URLRequest(url: URL(string: url)!)
    }
}

EffectOneModule.swift 源码

import  Foundation

import  UIKit

import  Toast

// {zh} 需要引入EffectOneKit {en} Need to introduce EffectOneKit
import  EffectOneKit

// {zh} 需要引入导出组件 {en} Export component needs to be introduced
import  EOExportUI

// {zh} 需要引入剪同款组件 {en} You need to introduce and cut the same components.
import  CutSameUIIF

class EffectOneModule  :  NSObject
{
    let  authFileName :  String = "com.volcengine.effectone.inhouse.licbag"
    // {zh} 存储鉴权状态 {en} store authentication state
    var  isAuthSucceeded:  Bool = false
    // {zh} 存储被使用的VC,用于弹出界面 {en} Store the used VC for the pop-up interface
    var  parentVC:  UIViewController
    
    // {zh} 暂存拍摄组件的应用,用于退出编辑组件时,隐藏相册选图页面 {en} The application that temporarily stores the shooting component is used to hide the album selection page when exiting the editing component
    private weak var  recorderVC:  UIViewController?
    
    init(parentVC:  UIViewController) {
        self.parentVC  =  parentVC
        }
    // {zh} 启动鉴权 {en} enable authentication
    public func makeAuth(){
        let  config  = EOAuthorizationConfig  { [self] initializer  in
            // {zh} 鉴权方式是离线鉴权 {en} The authentication method is offline authentication.
                        initializer.isOnline  = false
            // {zh} 设置离线证书所在的路径 {en} Set the path where the offline certificate is located
                        initializer.licensePathForOffline  = self.localBundle().path(forResource:  self.authFileName, ofType:  nil, inDirectory:  "License")!
                }
        // {zh} 开始鉴权 {en} Start authentication
        EOAuthorization.sharedInstance().makeAuth(with: config) {isSuccess, errMsg  in
            self.isAuthSucceeded  =  isSuccess
            if !self.isAuthSucceeded {
                // {zh} 鉴权失败打印错误码 {en} Authentication failed to print error code
                print(errMsg)
                        }
            else
                        {
                // {zh} 鉴权成功,初始化EOSDK及资源配置 {en} Authentication successful, initialize EOSDK and resource allocation
                EOSDK.initSDK { [weak self]  in
                    //  {zh} 设置资源根路径  {en} Set the resource root path
                    EOSDK.setResourceBaseDir(EOSDK.getEODocumentRootDir()  + "/download")
                    //  {zh} 设置配置文件根路径  {en} Set the configuration file root path
                    EOSDK.setResourceDefaultBuiltInConfig(EOSDK.defaultPanelConfigDir(self!.localBundle().bundlePath))
                    //  {zh} 设置内置资源路径  {en} Set built-in resource path
                    EOSDK.setBuiltInResourceDir(EOSDK.defaultResourceDir(self!.localBundle().bundlePath))
                    //  {zh} 注册剪同款获取资源协议  {en} Register to cut the same item to get the resource agreement
                    EOInjectContainer.shared().resourceRequestBuilderImpl  = EOCustomRequestBuilderImpl()
                    //  {zh} 设置从服务端获取  {en} Set fetch from server level
                    EOSDK.useRemoteConfig(true, useRemoteResource:  true)
                                }
                        }
                }
        }
    //  {zh} 进入基础编辑组件的源  {en} Access to the source of the basic editing component
    enum EditorParams  {
        case  normal(EORecordInfo)
        case  draft(EOSDKDraftModel)
        }
    
    // {zh} 获取资源根目录 {en} Get resource root directory
    private func localBundle() ->  Bundle  {
        Bundle(path:  Bundle.main.path(forResource:  "EOLocalResources", ofType:  "bundle")  ?? "")  ?? Bundle.main
        }
    
    private func showErrorAlert(_ err:  NSError,  presenter:  UIViewController) {
        let  msg  = "\(NSLocalizedString("eo_home_error_alert_title", comment:  ""))\(err.domain):\(err.code)"
        let  alert  = UIAlertController(title:  nil, message: msg, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title:  NSLocalizedString("eo_home_error_alert_ok", comment:  ""), style: .cancel))
                presenter.present(alert, animated:  true)
        }
    
    public func showToast(_ text:  String) {
        guard let  window  = UIApplication.shared.keyWindow  else  {
            return
                }
                window.makeToast(text, duration:  1.5, position:CSToastPositionCenter)
        }
    
    private func topMostViewController() ->  UIViewController? {
        var  topViewController:  UIViewController?
        let  window  = UIApplication.shared.keyWindow
        let  rootViewController  =  window?.rootViewController
        if let  tabBar  =  rootViewController  as? UITabBarController  {
                        topViewController  =  tabBar.selectedViewController
                }  else  {
                        topViewController  =  rootViewController;
                }
        if let  nav  =  topViewController  as? UINavigationController  {
                        topViewController  =  nav.topViewController
                }
        while  topViewController?.presentedViewController  != nil  {
                        topViewController  =  topViewController?.presentedViewController
            if let  nav  =  topViewController  as? UINavigationController  {
                                topViewController  =  nav.topViewController
                        }
                }
        return  topViewController
        }
    
    func showAlert(withTitle title:  String,
                   message:  String,
                   cancelTitle:  String?,
                   confirmTitle:  String?,
                   cancelHandler: (() ->  Void)?,
                   confirmHandler: (() ->  Void)?) {
        let  alert  = UIAlertController(title: title, message: message, preferredStyle: .alert)

        if let  cancelTitle  =  cancelTitle {
            let  cancelAction  = UIAlertAction(title: cancelTitle, style: .cancel) {  _ in
                                cancelHandler?()
                        }
                        alert.addAction(cancelAction)
                }

        if let  confirmTitle  =  confirmTitle {
            let  confirmAction  = UIAlertAction(title: confirmTitle, style: .default) {  _ in
                                confirmHandler?()
                        }
                        alert.addAction(confirmAction)
                }

        self.parentVC.present(alert, animated:  true, completion:  nil)
        }

}

extension EffectOneModule  {
    // {zh} 启动基础编辑组件 {en} Start the basic editing component
    public func showRecorderViewController() {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
        // {zh} 构造拍摄组件默认配置 {en} Construct the default configuration of the shooting component
        let  config  = EORecorderConfig  { initializer  in
            // config recorder if needed
            
                        initializer.configRecorderViewController { recorderVCInitializer  in
                // config recoderViewController if needed
                        }
                }
        // {zh} 显示拍摄页面 {en} Show the shooting page
        EORecorderViewController.startRecorder(with: config, presenter:  self.parentVC, delegate:  self) { [weak self] error  in
            if let  err  =  error  as? NSError,  let  presenter  = self?.parentVC {
                self?.showErrorAlert(err, presenter: presenter)
                        }
                }
        }
    // {zh} 视频合拍 {en} Video co-production
    public func showDuetViewController() {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
        // {zh} 检测相册权限 {en} Detect album permissions
        EOAlbumHelper.eoCheckAlbumAuthorized { [weak self]  in
            // {zh} 使用EOSDK中相册组件来实现选图功能 {en} Use the album component in EOSDK to realize the picture selection function
            guard let  resourcePicker  = EOInjectContainer.shared().resourcePickerSharedImpl  else  {
                return
                        }
            // {zh} 选择相册素材结束 {en} Select album material to end
                        resourcePicker.pickVideoForDuet(completion: { [weak self] resources, error, cancel  in
                if let  resorce  =  resources.first {
                    // {zh} 进入合拍页面 {en} Enter the match page
                    self?.gotoDuetViewController(withVideoURL: resorce.url!)
                                }
                
                        })
            
                } restrictedOrDenied: { [weak self]  in
            self?.showAlert(withTitle:  NSLocalizedString("eo_home_camera_album_unauth_alert_title", comment:  ""),
                                                                    message:  NSLocalizedString("eo_home_camera_album_unauth_alert_message", comment:  ""),
                                                                    cancelTitle:  NSLocalizedString("eo_home_camera_unauth_alert_cancel", comment:  ""),
                                                                    confirmTitle:  NSLocalizedString("eo_home_camera_unauth_alert_confirm", comment:  ""),
                                                                    cancelHandler:  nil,
                                                                    confirmHandler: {
                if #available(iOS 10.0,  *) {
                    UIApplication.shared.open(URL(string:  UIApplication.openSettingsURLString)!, options: [:]) { success  in
                        // Completion handler
                                        }
                                }  else  {
                    UIApplication.shared.openURL(URL(string:  UIApplication.openSettingsURLString)!)
                                }
                        })
                }

        
        }
    // {zh} 进入合拍页面 {en} Enter the match page
    public func gotoDuetViewController(withVideoURL videoURL:URL) {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
        
                
        let  config  = EORecorderConfig  { initializer  in
            // config recorder if needed
                        initializer.configRecorderViewController { recorderVCInitializer  in
                // config recoderViewController if needed
                                recorderVCInitializer.musicBarHidden  = true
                let  sideConfig  = EORecorderSideBarConfig()
                                sideConfig.itemKeys  =  [EORecordSideBarItemKey.barItemRotateCameraKey,
                                       EORecordSideBarItemKey.barItemFlashKey,
                                       EORecordSideBarItemKey.barItemMicKey,
                                       EORecordSideBarItemKey.barItemDuetLayoutKey,
                                       EORecordSideBarItemKey.barItemTimerKey,
                                       EORecordSideBarItemKey.barItemFiltersKey,
                                       EORecordSideBarItemKey.barItemBeautyKey,
                                       EORecordSideBarItemKey.barItemSpeedKey,]
                                recorderVCInitializer.sideBarConfig  =  sideConfig
                        }
                }
        
        EODuetViewController.startDuet(with: config, presenter:  self.parentVC,duetVideoURL:videoURL, delegate:  self, completion: { [weak self] error  in
            if let  err  =  error  as? NSError,  let  presenter  = self?.parentVC {
                self?.showErrorAlert(err, presenter: presenter)
                        }
                })
        }
    
    // {zh} 进入剪同款页面 {en} Enter CutSame page
    func showCutSameController() {
        CutSameRouter.toCutSameCategoryVC()
        }

}

extension EffectOneModule:  EORecorderViewControllerDelegate  {
    // {zh} 显示相册页面 {en} Show album page
    func recorderViewControllerDidTapAlbum(_ recorderViewController:  EORecorderViewController) {
        // {zh} 暂存拍摄组件的应用,用于退出编辑组件时,隐藏相册选图页面 {en} The application that temporarily stores the shooting component is used to hide the album selection page when exiting the editing component
        self.recorderVC  =  recorderViewController
        
        // {zh} 使用EOSDK中相册组件来实现选图功能 {en} Use the album component in EOSDK to realize the picture selection function
        guard let  resourcePicker  = EOInjectContainer.shared().resourcePickerSharedImpl  else  {
            return
                }
        
                recorderViewController.pausePreview()
        
                resourcePicker.pickResourcesFromRecorder { [weak self] resources, error, cancel  in
            guard !resources.isEmpty  else  {
                return;
                        }
            
            let  info  = EORecordInfo()
                        info.mediaAssets  =  resources
                        info.source  =  .album
            // {zh} 完成选图后显示编辑页面 {en} Show the edit page after completing the selection
            if let  presenter  = self?.topMostViewController() {
                self?.showEditorViewController(.normal(info), presenter: presenter)
                        }
                }
        }
    
    // {zh} 完成拍摄后,跳转进入编辑页面 {en} After finishing shooting, jump to the editing page
    func recorderViewController(_ recorderViewController:  EORecorderViewController,  didFinishRecordingMediaWith info:  EORecordInfo) {
        // {zh} 暂存拍摄组件的应用,用于退出编辑组件时,隐藏相册选图页面 {en} The application that temporarily stores the shooting component is used to hide the album selection page when exiting the editing component
        self.recorderVC  =  recorderViewController
        
                recorderViewController.pausePreview()
        // {zh} 进入基础编辑 {en} Enter basic editing
        self.showEditorViewController(.normal(info), presenter: recorderViewController)
        }

}

extension EffectOneModule  {
    // {zh} 拍摄完成、从相册或者草稿进入基础编辑 {en} The shooting is completed or enter the basic editing from the album.
    func showEditorViewController(_ params:  EditorParams,  presenter:  UIViewController) {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
        // {zh} 构造编辑组件默认配置 {en} Construct editing component default configuration
        let  config  = EOEditorConfig  { initializer  in}
        // {zh} 设置编辑组件需要的输入参数 {en} Set the input parameters required by the editing component
        let  sceneConfig  = EOEditorSceneConfig()
        switch  params {
        case  .normal(let  info):// {zh} 从拍摄或相册进入基础编辑 {en} Go from shooting or album to basic editing
                        sceneConfig.resources  =  info.mediaAssets
                        sceneConfig.backgroundMusic  =  info.backgroundMusic
                        sceneConfig.coverImage  =  info.coverImage
                        sceneConfig.previewContentMode  =  info.source  ==  .camera  ?  .aspectFill : .aspectFit
                        sceneConfig.fromType  =  info.source  ==  .camera  ? "camera"  :  "album"
            // {zh} 判断是否合拍 {en} Determine whether it is in tune
            if  info.isDuet  == true    {
                                sceneConfig.editorPreviewForType  =  .duetEditor
                                sceneConfig.duetMusic  =  info.duetUrl
                        }
        case  .draft(let  model):// {zh} 从草稿进入基础编辑 {en} From draft to basic editing
                        sceneConfig.draftModel  =  model
                }
        // {zh} 显示编辑页面 {en} Show edit page
        EOVideoEditorViewController.startEditor(with: config, sceneConfig: sceneConfig, presenter: presenter, delegate:  self) { [weak self,  weak  presenter] error  in
            if let  err  =  error  as? NSError,  let  vc  =  presenter {
                self?.showErrorAlert(err, presenter: vc)
                        }
                };
        }
    // {zh} 启动草稿组件方法 {en} Start draft component method
    public func showDraftViewController() {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
        // {zh} 显示草稿组件 {en} Show draft component
        EODraftBoxController.presentDraftVCDelegate(self)
        }
    //{zh} 调用高光成片     {en} Call highlight film
    public func showHighLightViewController() {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
                _showHighLightViewController()
        }

}

// MARK: highLightFirm
extension EffectOneModule  {
    public func _showHighLightViewController() {
        if !self.isAuthSucceeded {
            self.showToast("authority is failed!,please check it.")
            return
                }
        EOAlbumHelper.eoCheckAlbumAuthorized {// {zh} 相册权限已打开 {en} Album permissions are turned on
            // {zh} 启动高光成片 {en} Start highlight film
            EOHighLightManager.toHighLight()
                } restrictedOrDenied: { [weak self]  in
            self?.showAlert(withTitle:  NSLocalizedString("eo_home_camera_album_unauth_alert_title", comment:  ""),
                                                                    message:  NSLocalizedString("eo_home_camera_album_unauth_alert_message", comment:  ""),
                                                                    cancelTitle:  NSLocalizedString("eo_home_camera_unauth_alert_cancel", comment:  ""),
                                                                    confirmTitle:  NSLocalizedString("eo_home_camera_unauth_alert_confirm", comment:  ""),
                                                                    cancelHandler:  nil,
                                                                    confirmHandler: {
                if #available(iOS 10.0,  *) {
                    UIApplication.shared.open(URL(string:  UIApplication.openSettingsURLString)!, options: [:]) { success  in
                        // Completion handler
                                        }
                                }  else  {
                    UIApplication.shared.openURL(URL(string:  UIApplication.openSettingsURLString)!)
                                }
                        })
                }

        }

}

extension EffectOneModule:  EOVideoEditorViewControllerDelegate  {
    func videoEditorViewControllerDidCancel(_ videoEditorViewController:  EOVideoEditorViewController) {
        if let  recorder  =  recorderVC {
            // {zh} 隐藏相册选图页面 {en} Hide album selection page
                        recorder.dismiss(animated:  true, completion:  nil)
                }  else  {
            // {zh} 当从草稿进入编辑页面再退出编辑时,隐藏编辑页面 {en} Hide the edit page when entering the edit page from the draft and then exiting the edit
                        videoEditorViewController.dismiss(animated:  true, completion:  nil)
                }
        }
    
    // {zh} 点击下一步按钮,启动导出页面 {en} Click the Next button to launch the export page
    func videoEditorViewControllerTapNext(_ exportModel:  EOExportModel,  presentVC viewController:  UIViewController) {
        EOExportViewController.startExport(with: exportModel, presentVC: viewController)
        }

}

extension EffectOneModule:  EODraftBoxControllerDelegate  {
    // {zh} 从草稿进入基础编辑组件 {en} Moving from draft to basic editing components
    func draftBoxController(_ draftBoxController:  EODraftBoxController,  didSelectDraft draftModel:  EOSDKDraftModel) {
        self.showEditorViewController(.draft(draftModel), presenter: draftBoxController)
        }

}