目前CK SDK不支持在pod中以动态库形式引入,所以如果您的项目在Podfile中添加了use_frameworks!
,需要删掉。
# {zh} 请关闭use_frameworks! {en} Disable use_frameworks! use_modular_headers! # {zh} CKSDK目录与Podfile的相对路径 {en} CKSDK relative path to Podfile $CKSDK_RELATIVE_PATH = "./CKSDK" # {zh} 通用的 pods def base_pods pod 'TTVideoEditor', '11.8.1.29-D',:source => 'https://github.com/volcengine/volcengine-specs.git' pod 'NLEPlatform', '0.5.2', :source => 'https://github.com/volcengine/volcengine-specs.git' pod 'DVEInject', '0.0.5', :source => 'https://github.com/volcengine/volcengine-specs.git' # supports pod 'CKi18n', :path => "#$CKSDK_RELATIVE_PATH/CKi18n", :modular_headers => false pod 'CKResource', :path => "#$CKSDK_RELATIVE_PATH/CKResource", :modular_headers => false end # {zh} 编辑 def editor_pods base_pods # 接入时,根据项目实际情况,调整相对路径 pod 'NLEEditor', :subspecs => ['CKStandard'], :path => "#$CKSDK_RELATIVE_PATH/NLEEditor-iOS", :modular_headers => false pod 'DVETrackKit', :subspecs => ['CKStandard'], :path => "#$CKSDK_RELATIVE_PATH/DVETrackKit", :modular_headers => false pod 'DVEFoundationKit', :path => "#$CKSDK_RELATIVE_PATH/DVEFoundationKit", :modular_headers => false pod 'CKEditor', :subspecs => ['CKStandard'], :path => "#$CKSDK_RELATIVE_PATH/CKEditor", :modular_headers => false ################################### 第三方 ################################### pod 'SGPagingView', '1.7.1' pod 'lottie-ios', '2.5.3' pod 'FileMD5Hash' pod 'SDWebImage' end # {zh} 拍摄 {en} Recorder def recorder_pods base_pods pod 'CKRRecorder', :subspecs => ['Arch'], :path => "#$CKSDK_RELATIVE_PATH/CKRRecorder", :modular_headers => false end target 'CKDemo-Swift' do # 基础编辑 & 拍摄 editor_pods recorder_pods end # {zh} 修改一些编译参数,优化开发体验,可按需选择 {en} optional post_install do |installer| installer.generated_projects.each do |project| project.targets.each do |target| target.build_configurations.each do |config| # {zh} 解决 Xcode14 编译问题 {en} [Xcode 14 build failed with manual code sign and app resource bundles](https://github.com/CocoaPods/CocoaPods/issues/11402) config.build_settings['CODE_SIGN_IDENTITY'] = '-' config.build_settings['CODE_SIGN_ENTITLEMENTS'] = '-' config.build_settings['CODE_SIGNING_REQUIRED'] = 'NO' config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' end end project.build_configurations.each do |config| config.build_settings['CLANG_WARN_DOCUMENTATION_COMMENTS'] = 'NO' # 禁止部分 warning config.build_settings['WARNING_CFLAGS'] = '-Wno-strict-prototypes -Wno-swift-name-attribute -Wno-comment -Wno-shorten-64-to-32 -Wno-unused-function -Wno-unused-variable -Wno-nullability -Wno-nullability-completeness -Wno-documentation -Wno-documentation-html -Wno-incomplete-umbrella' # 禁止全部 warning if project.targets.first.name.start_with?("NLEPlatform") config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = "YES" end # 减少 warning 造成的 Xcode 卡顿 if project.targets.first.name.start_with?("NLEEditor") || project.targets.first.name.start_with?("DVETrackKit") config.build_settings['WARNING_CFLAGS'] = '-Wno-strict-prototypes -Wno-swift-name-attribute -Wno-comment -Wno-shorten-64-to-32 -Wno-unused-function -Wno-unused-variable -Wno-nullability -Wno-nullability-completeness -Wno-documentation -Wno-documentation-html -Wno-ambiguous-macro' end end end end
在CocoaPods 1.5.0之后,引入Swift pod也不再强制要求打开use_frameworks!
,并可使用use_modular_headers!
来支持module。可参考:https://blog.cocoapods.org/CocoaPods-1.5.0/。
如果您有一些pod组件必须以动态库形式引入,可在Podfile中参考如下方式处理:
dynamic_frameworks = ['Alamofire','SnapKit'] pre_install do |installer| # Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {} installer.pod_targets.each do |target| if dynamic_frameworks.include?(target.name) def target.build_type Pod::BuildType.dynamic_framework end end end end
将CK SDK copy到您项目与Podfile同级的目录下(与上面Podfile中的配置匹配)
进入 TARGETS > Project Name >Build Setting
选择 All ,搜索 bitcode
Enable Bitcode 选择 NO
找到项目中的 info.plist 文件
点击 【 + 】 添加音频和视频设备权限:
Privacy - Microphone Usage Description ,并填入使用麦克风的原因( Value )
Privacy - Camera Usage Description ,并填入使用摄像头的原因( Value )
Privacy - Media Library Usage Description,并填入使用播放原因( Value )
Privacy - Photo Library Additions Usage Description,并填入使用相册原因( Value )
Privacy - Photo Library Usage Description,并填入使用相册的原因( Value )
YourApp-Swift-Bridging-Header,并将以下头文件导入
// YourApp-Swift-Bridging-Header #import <CKEditor/CKEditor.h> #import <DVEFoundationKit/UIViewController+Private.h> #import <CKEditor/CKRResourceLoader.h> // recorder #import <CKRRecorder/CKRRecorderViewController.h> // duet #import <CKRRecorder/CKRResourcePicker.h> #import <CKRRecorder/CKRDuetViewController.h> // edit #import <CKRRecorder/CKRResourcePicker.h> #import <NLEEditor/DVEUIFactory.h> // draft #import <CKEditor/CKEDraftBoxController.h> // result callback #import <NLEEditor/DVEVCContextExternalInjectProtocol.h>
将我们提供给您的资源替换到以下三个目录中,执行pod install然后初始化
```objectivec
func initCKSDK() {
let config = CKEditorEngineConfig.default()
config.veLicensePath = Bundle.main.path(forResource: "com.bytedance.solution.ck.licbag", ofType: "") ?? ""
config.veAppKey = "iWwiXvXhlN"
config.veToken = "cJ75jX6c9m8eObgQif7okMgb3U2398MrE1DCr1LH1neWx3x4BkL5hIqlRwbrC1Hf6aVQRcvSWrFuXbf8ROmHIHTz1X5daYyh6Gmb+J/cI1deUayXIgmE2KUIlYyjMgDHL0gbyUNl6+XJhspB1nZP6tHyDfBhSISdQ5Aq6/1zqilrPYJSjo547IkdVg2xfuhDACrbgs1iu9LqI7pHDc+JEOSTYuHA3Fe5aVyVkwBRqBNf1EgrsgMtZ1W1U2GAZpLMQke7DUvB7WDaAb+GdZSAcovLM8WjGzZiIV4Cj6YTFYBtNH3WfM15954YRcS1rOeYhPmZN7/N6yYmBBz3OZKx/sD8OyFk5cY6Q9PdaqDF14qPhUIy0rqX79tqxNwyOLnKYWR+e/8nrG2N8NDef9BZ7dDSREVkgiDl8ELmDgKYzraUxyy5MOV1hbITl6nieiuI/oeVu0z3snytsDt4yMyOdsaT3acngkJJhHX3tz2dh+R5v2diJW6+VsybE6166eJm5swVLAxqdn1wcQuPX64hKHXuc6SssjwddRKqMgKrCzHrGdW5IJKQC25lOdY4ENMnSV8HwRUaaEZQJPBIyC605H6VQCDwUTHGL4VKu5enIA/itdoYJY8lJ9YQ7ZSWFZO21gTaAPhEj1JuuToYg6wh9xIPgFhKbpLknJzweKKxyzgtM3Ndf6Z4czyFrCKhJgZm+yEkl5/CplXWJ/MZT2tH9yIjnZlySfUHZFwJtB78MNdrxd3p+ux/me2CK70uvSBAB7pXautnEEpQdkQ3TDSJEgMvMN1QGZ09LaDPOINWjH2xl2aNCFMkB7Q5p1AqJFu69tZjzwRorFvLETwJNpBXZSP9M660/86y5MRPhvbFgWIrZwhM6/sf66/3MoRyz5cmwQ9fiYNheTCPLs+TVAkvKulQFZnSF2PgcuEBgqFCR03pcS2wn6C/QjMbIIa8B26aImgPzBzi9WqEjZjoVrb1OxkkSsJZFpvLWXr1YQslRut4sR9zldy1BijVN2AJdKnP2D1Zu9za/HodeX7dskbe2tiTX4xbfNCFPbAvubtI9UlOMsTOTdmnG7GRPykSsEO1fPmIEx0YeSlf6M1whjRzPQhhhqs3cnn8QbhDcpRj/Big3+PWeNf7c+vPjPBSYbXIYS2cNE5grP56/1Pv+VqftNMQP5dHIQJ7A1iXPabP0VPsgrlpH5KnBe/bX4jDLgP4FUWG4jl/YroGO0ETSwUWboldc6ia45pgcOCibQ9Gx/PmGdP2JzYx6QyImo3TwbDrhWhCMqYtKgwpPo+UD9lh4Q=="
/// {zh} 可选配置:根据需要的功能,联系火山引擎提供 {en} Optional: According to the required functions, contact Volcano Engine to provide config.subtitleAppId = "" config.subtitleToken = "" config.volcAccessKey = "" config.volcSecretKey = "" CKEditorEngine.shared().start(with: config) }
3. #### 跳转页面 ```objectivec @IBAction func goRecord(_ sender: Any) { let vc = CKRRecorderViewController() vc.injectedContainer = CustomServiceContainer() let nav = UINavigationController.init(rootViewController: vc) nav.modalPresentationStyle = .fullScreen present(nav, animated: true) } @IBAction func goDuet(_ sender: Any) { CKRResourcePicker().pickResources { [weak self] resources, error, cancel in let vc = CKRDuetViewController() let model = resources.first guard let url = model?.url() else { return } vc.duetURL = url vc.injectedContainer = CustomServiceContainer() let nav = UINavigationController.init(rootViewController: vc) nav.modalPresentationStyle = .fullScreen nav.modalPresentationStyle = .fullScreen self?.present(nav, animated: true) } } @IBAction func goEdit(_ sender: Any) { CKRResourcePicker().pickResources { [weak self] resources, error, cancel in if resources.count <= 0 { return } let vc = DVEUIFactory.createDVEViewController(withResources: resources, injectService: CustomServiceContainer()) self?.navigationController?.pushViewController(vc, animated: true) } } @IBAction func goDraft(_ sender: Any) { let vc = CKEDraftBoxController() vc.serviceInjectContainer = CustomServiceContainer() self.present(vc, animated: true) }
如下新增一个回调类,在editorDidExportedVideo中获取导出的视频
import UIKit @objc class CustomServiceContainer: NSObject, DVEVCContextExternalInjectProtocol { lazy var resourceLoader: DVEResourceLoaderProtocol = { return CKRResourceLoader() }() lazy var eventDelagate: DVEEditorEventProtocol = { return CustomEditorEventDelegate() }() func provideResourceLoader() -> DVEResourceLoaderProtocol { resourceLoader } func provideEditorEvent() -> DVEEditorEventProtocol { eventDelagate } } class CustomEditorEventDelegate: NSObject, DVEEditorEventProtocol { func editorDidExportedVideo(_ viewController: UIViewController, result success: Bool, videoURL url: URL?, draftID: String) { if success, let url = url { // Here you can process the video on demand UISaveVideoAtPathToSavedPhotosAlbum(url.path, nil, nil, nil) } // And then close SDK viewController.dve_close(animated: true) } }
暂时无法在飞书文档外展示此内容
CK SDK does not support imported as dynamic frameworks currently, so if your project adds use_frameworks!
in the Podfile, you need to delete it.
# {zh} 请关闭use_frameworks! {en} Disable use_frameworks! use_modular_headers! # {zh} CKSDK目录与Podfile的相对路径 {en} CKSDK relative path to Podfile $CKSDK_RELATIVE_PATH = "./CKSDK" # {zh} 通用的 pods def base_pods pod 'TTVideoEditor', '11.8.1.29-D',:source => 'https://github.com/volcengine/volcengine-specs.git' pod 'NLEPlatform', '0.5.2', :source => 'https://github.com/volcengine/volcengine-specs.git' pod 'DVEInject', '0.0.5', :source => 'https://github.com/volcengine/volcengine-specs.git' # supports pod 'CKi18n', :path => "#$CKSDK_RELATIVE_PATH/CKi18n", :modular_headers => false pod 'CKResource', :path => "#$CKSDK_RELATIVE_PATH/CKResource", :modular_headers => false end # {zh} 编辑 def editor_pods base_pods # 接入时,根据项目实际情况,调整相对路径 pod 'NLEEditor', :subspecs => ['CKStandard'], :path => "#$CKSDK_RELATIVE_PATH/NLEEditor-iOS", :modular_headers => false pod 'DVETrackKit', :subspecs => ['CKStandard'], :path => "#$CKSDK_RELATIVE_PATH/DVETrackKit", :modular_headers => false pod 'DVEFoundationKit', :path => "#$CKSDK_RELATIVE_PATH/DVEFoundationKit", :modular_headers => false pod 'CKEditor', :subspecs => ['CKStandard'], :path => "#$CKSDK_RELATIVE_PATH/CKEditor", :modular_headers => false ################################### 第三方 ################################### pod 'SGPagingView', '1.7.1' pod 'lottie-ios', '2.5.3' pod 'FileMD5Hash' pod 'SDWebImage' end # {zh} 拍摄 {en} Recorder def recorder_pods base_pods pod 'CKRRecorder', :subspecs => ['Arch'], :path => "#$CKSDK_RELATIVE_PATH/CKRRecorder", :modular_headers => false end target 'CKDemo-Swift' do # 基础编辑 & 拍摄 editor_pods recorder_pods end # {zh} 修改一些编译参数,优化开发体验,可按需选择 {en} optional post_install do |installer| installer.generated_projects.each do |project| project.targets.each do |target| target.build_configurations.each do |config| # {zh} 解决 Xcode14 编译问题 {en} [Xcode 14 build failed with manual code sign and app resource bundles](https://github.com/CocoaPods/CocoaPods/issues/11402) config.build_settings['CODE_SIGN_IDENTITY'] = '-' config.build_settings['CODE_SIGN_ENTITLEMENTS'] = '-' config.build_settings['CODE_SIGNING_REQUIRED'] = 'NO' config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' end end project.build_configurations.each do |config| config.build_settings['CLANG_WARN_DOCUMENTATION_COMMENTS'] = 'NO' # 禁止部分 warning config.build_settings['WARNING_CFLAGS'] = '-Wno-strict-prototypes -Wno-swift-name-attribute -Wno-comment -Wno-shorten-64-to-32 -Wno-unused-function -Wno-unused-variable -Wno-nullability -Wno-nullability-completeness -Wno-documentation -Wno-documentation-html -Wno-incomplete-umbrella' # 禁止全部 warning if project.targets.first.name.start_with?("NLEPlatform") config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = "YES" end # 减少 warning 造成的 Xcode 卡顿 if project.targets.first.name.start_with?("NLEEditor") || project.targets.first.name.start_with?("DVETrackKit") config.build_settings['WARNING_CFLAGS'] = '-Wno-strict-prototypes -Wno-swift-name-attribute -Wno-comment -Wno-shorten-64-to-32 -Wno-unused-function -Wno-unused-variable -Wno-nullability -Wno-nullability-completeness -Wno-documentation -Wno-documentation-html -Wno-ambiguous-macro' end end end end
After CocoaPods 1.5.0, dependency of Swift pod is no longer need to enable use_frameworks!
in Podfile, and can use use_modular_headers!
to support modules. Refer to:https://blog.cocoapods.org/CocoaPods-1.5.0/
If you have some pods that must be imported as dynamic frameworks, you can refer to the following methods in Podfile:
dynamic_frameworks = ['Alamofire','SnapKit'] pre_install do |installer| # Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {} installer.pod_targets.each do |target| if dynamic_frameworks.include?(target.name) def target.build_type Pod::BuildType.dynamic_framework end end end end
Copy the CK SDK to the directory of your project at the same level as the Podfile (matching the configuration in the Podfile above)
TARGETS > Project Name >Build Setting
Select All ,search bitcode
Enable Bitcode: NO
Find the info.plist in your project
Click 【 + 】 to add privacy usage description
Privacy - Microphone Usage Description ,并填入使用麦克风的原因( Value )
Privacy - Camera Usage Description ,并填入使用摄像头的原因( Value )
Privacy - Media Library Usage Description,并填入使用播放原因( Value )
Privacy - Photo Library Additions Usage Description,并填入使用相册原因( Value )
Privacy - Photo Library Usage Description,并填入使用相册的原因( Value )
YourApp-Swift-Bridging-Header. Import the following header files
// YourApp-Swift-Bridging-Header #import <CKEditor/CKEditor.h> #import <DVEFoundationKit/UIViewController+Private.h> #import <CKEditor/CKRResourceLoader.h> // recorder #import <CKRRecorder/CKRRecorderViewController.h> // duet #import <CKRRecorder/CKRResourcePicker.h> #import <CKRRecorder/CKRDuetViewController.h> // edit #import <CKRRecorder/CKRResourcePicker.h> #import <NLEEditor/DVEUIFactory.h> // draft #import <CKEditor/CKEDraftBoxController.h> // result callback #import <NLEEditor/DVEVCContextExternalInjectProtocol.h>
Replace the resources we provide you into the following three directories, execute pod install
and then initialize
```objectivec
func initCKSDK() {
let config = CKEditorEngineConfig.default()
config.veLicensePath = Bundle.main.path(forResource: "com.bytedance.solution.ck.licbag", ofType: "") ?? ""
config.veAppKey = "iWwiXvXhlN"
config.veToken = "cJ75jX6c9m8eObgQif7okMgb3U2398MrE1DCr1LH1neWx3x4BkL5hIqlRwbrC1Hf6aVQRcvSWrFuXbf8ROmHIHTz1X5daYyh6Gmb+J/cI1deUayXIgmE2KUIlYyjMgDHL0gbyUNl6+XJhspB1nZP6tHyDfBhSISdQ5Aq6/1zqilrPYJSjo547IkdVg2xfuhDACrbgs1iu9LqI7pHDc+JEOSTYuHA3Fe5aVyVkwBRqBNf1EgrsgMtZ1W1U2GAZpLMQke7DUvB7WDaAb+GdZSAcovLM8WjGzZiIV4Cj6YTFYBtNH3WfM15954YRcS1rOeYhPmZN7/N6yYmBBz3OZKx/sD8OyFk5cY6Q9PdaqDF14qPhUIy0rqX79tqxNwyOLnKYWR+e/8nrG2N8NDef9BZ7dDSREVkgiDl8ELmDgKYzraUxyy5MOV1hbITl6nieiuI/oeVu0z3snytsDt4yMyOdsaT3acngkJJhHX3tz2dh+R5v2diJW6+VsybE6166eJm5swVLAxqdn1wcQuPX64hKHXuc6SssjwddRKqMgKrCzHrGdW5IJKQC25lOdY4ENMnSV8HwRUaaEZQJPBIyC605H6VQCDwUTHGL4VKu5enIA/itdoYJY8lJ9YQ7ZSWFZO21gTaAPhEj1JuuToYg6wh9xIPgFhKbpLknJzweKKxyzgtM3Ndf6Z4czyFrCKhJgZm+yEkl5/CplXWJ/MZT2tH9yIjnZlySfUHZFwJtB78MNdrxd3p+ux/me2CK70uvSBAB7pXautnEEpQdkQ3TDSJEgMvMN1QGZ09LaDPOINWjH2xl2aNCFMkB7Q5p1AqJFu69tZjzwRorFvLETwJNpBXZSP9M660/86y5MRPhvbFgWIrZwhM6/sf66/3MoRyz5cmwQ9fiYNheTCPLs+TVAkvKulQFZnSF2PgcuEBgqFCR03pcS2wn6C/QjMbIIa8B26aImgPzBzi9WqEjZjoVrb1OxkkSsJZFpvLWXr1YQslRut4sR9zldy1BijVN2AJdKnP2D1Zu9za/HodeX7dskbe2tiTX4xbfNCFPbAvubtI9UlOMsTOTdmnG7GRPykSsEO1fPmIEx0YeSlf6M1whjRzPQhhhqs3cnn8QbhDcpRj/Big3+PWeNf7c+vPjPBSYbXIYS2cNE5grP56/1Pv+VqftNMQP5dHIQJ7A1iXPabP0VPsgrlpH5KnBe/bX4jDLgP4FUWG4jl/YroGO0ETSwUWboldc6ia45pgcOCibQ9Gx/PmGdP2JzYx6QyImo3TwbDrhWhCMqYtKgwpPo+UD9lh4Q=="
/// {zh} 可选配置:根据需要的功能,联系火山引擎提供 {en} Optional: According to the required functions, contact Volcano Engine to provide config.subtitleAppId = "" config.subtitleToken = "" config.volcAccessKey = "" config.volcSecretKey = "" CKEditorEngine.shared().start(with: config) }
3. #### Jump to pages of SDK After SDK initialized. ```objectivec @IBAction func goRecord(_ sender: Any) { let vc = CKRRecorderViewController() vc.injectedContainer = CustomServiceContainer() let nav = UINavigationController.init(rootViewController: vc) nav.modalPresentationStyle = .fullScreen present(nav, animated: true) } @IBAction func goDuet(_ sender: Any) { CKRResourcePicker().pickResources { [weak self] resources, error, cancel in let vc = CKRDuetViewController() let model = resources.first guard let url = model?.url() else { return } vc.duetURL = url vc.injectedContainer = CustomServiceContainer() let nav = UINavigationController.init(rootViewController: vc) nav.modalPresentationStyle = .fullScreen nav.modalPresentationStyle = .fullScreen self?.present(nav, animated: true) } } @IBAction func goEdit(_ sender: Any) { CKRResourcePicker().pickResources { [weak self] resources, error, cancel in if resources.count <= 0 { return } let vc = DVEUIFactory.createDVEViewController(withResources: resources, injectService: CustomServiceContainer()) self?.navigationController?.pushViewController(vc, animated: true) } } @IBAction func goDraft(_ sender: Any) { let vc = CKEDraftBoxController() vc.serviceInjectContainer = CustomServiceContainer() self.present(vc, animated: true) }
Add a callback class as follows to get the exported video in the function: editorDidExportedVideo
import UIKit @objc class CustomServiceContainer: NSObject, DVEVCContextExternalInjectProtocol { lazy var resourceLoader: DVEResourceLoaderProtocol = { return CKRResourceLoader() }() lazy var eventDelagate: DVEEditorEventProtocol = { return CustomEditorEventDelegate() }() func provideResourceLoader() -> DVEResourceLoaderProtocol { resourceLoader } func provideEditorEvent() -> DVEEditorEventProtocol { eventDelagate } } class CustomEditorEventDelegate: NSObject, DVEEditorEventProtocol { func editorDidExportedVideo(_ viewController: UIViewController, result success: Bool, videoURL url: URL?, draftID: String) { if success, let url = url { // Here you can process the video on demand UISaveVideoAtPathToSavedPhotosAlbum(url.path, nil, nil, nil) } // And then close SDK viewController.dve_close(animated: true) } }
暂时无法在飞书文档外展示此内容