×

ios swift

ios swift(iOS swift 实现摄影视频 保存到本地)

admin admin 发表于2024-03-07 13:18:36 浏览27 评论0

抢沙发发表评论

其实ios swift的问题并不复杂,但是又很多的朋友都不太了解iOS swift 实现摄影视频 保存到本地,因此呢,今天小编就来为大家分享ios swift的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!

本文目录

iOS swift 实现摄影视频 保存到本地

实现摄影视频  保存到本地 import UIKit import AVFoundation import Photos class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate { // 视频输出     let fileOutput = AVCaptureMovieFileOutput() // 录像按钮     var recordButton: UIButton! // 正在录音     var isRecording = false     override func viewDidLoad() {         super .viewDidLoad() // 设置预览画面         setUpPreview()     }func setUpPreview() {         let videoDevice =AVCaptureDevice.default(for:AVMediaType.video)         let audioDevice =AVCaptureDevice.default(for:AVMediaType.audio)         do {             if videoDevice== nil ||audioDevice== nil {                 throw NSError(domain:"device error", code:-1, userInfo: nil )             }             let captureSession =AVCaptureSession()             // video inputを capture sessionに追加             let videoInput = try AVCaptureDeviceInput(device: videoDevice!)             captureSession.addInput(videoInput)             // audio inputを capture sessionに追加             let audioInput = try AVCaptureDeviceInput(device: audioDevice!)             captureSession.addInput(audioInput)             // max 30sec             self .fileOutput.maxRecordedDuration=CMTimeMake(value:30, timescale:1)             captureSession.addOutput(fileOutput)             // プレビュー             let videoLayer :AVCaptureVideoPreviewLayer=AVCaptureVideoPreviewLayer(session: captureSession)             videoLayer.frame= self .view.bounds             videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill             self .view.layer.addSublayer(videoLayer)             captureSession.startRunning()             setUpButton()         } catch {             // エラー処理         }     }     func setUpButton() {         recordButton=UIButton(frame:CGRect(x:0,y:0,width:120,height:50))         recordButton.backgroundColor = UIColor.gray         recordButton.layer.masksToBounds = true         recordButton.setTitle("録画开始", for:UIControl.State.normal)         recordButton.layer.cornerRadius = 20.0         recordButton.layer.position = CGPoint(x: self .view.bounds.width/2, y: self .view.bounds.height-50)         recordButton.addTarget( self , action: #selector (ViewController.onClickRecordButton(sender:)), for: .touchUpInside)         self .view.addSubview(recordButton)     }     @objc func onClickRecordButton(sender: UIButton) {         if !isRecording{             // 録画开始             let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true )             let documentsDirectory = paths as String             let filePath :String? ="\(documentsDirectory)/temp.mp4"             let fileURL :NSURL=NSURL(fileURLWithPath: filePath!)             fileOutput.startRecording(to: fileURL as URL, recordingDelegate: self )             isRecording= true             changeButtonColor(target:recordButton, color:UIColor.red)             recordButton.setTitle("録画中", for: .normal)         } else {             // 録画终了             fileOutput.stopRecording()             isRecording= false             changeButtonColor(target:recordButton, color:UIColor.gray)             recordButton.setTitle("録画开始", for: .normal)         }     }     func  changeButtonColor(target:UIButton, color:UIColor) {         target.backgroundColor= color     }     func fileOutput( _ output:AVCaptureFileOutput, didFinishRecordingTo outputFileURL:URL, from connections: , error:Error?) {         // ライブラリへ保存         PHPhotoLibrary.shared().performChanges({             PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)         }) { completed, error in             if completed {                 print("Video is saved!")             }         }     } }

iOS(Swift) 基于 Moya 的二次封装

Moya+Alamofire是现阶段大部分 Swift 项目所喜欢使用的网络层框架,其简洁明了的协议式接口设计,非常让人上头. 但是项目中,一般都会基于这个框架再进行二次封装,以适用于公司业务.本篇文章就是讲解下我司所封装的框架(大部分一致,但有部分是自己的修改) 先说说网络层封装的最终目的,我们希望我们封装的请求框架,调用简单方便,封装简洁清晰易读,易拖展,本身已经具备了基础的加密,debug 打印,业务错误码处理等等功能. 以此为目的,一步步分析下如何封装. 最基础接入: 这种调用弊端很大,我们一般会去做二次的封装,这里讲解下我司封装的网络层框架(我单独把公司框架网络层提取出来,自己做了一点修改). 流程走,先封装 TargetType 这里对 targetType 进行拖展.我们不希望对外暴露 Moya 接口,所有关于 moya 的结构,都进行了二次封装. APIRoute 是对 method 的二次封装,顺便把 path 也封装进去 Env 是环境配置,属于公司业务范畴,这里不作展示. 我司使用的响应式框架为 ReactiveSwift Moya 对此的拖展函数为 在此基础上,我们进行封装 我做了一些业务筛减,保留存储的请求处理,说明都在code 里,这里有几个参数定义 在 plugins 基础上,我们定义了一个新的概念,APIPlugin,并且生命周期由rac 控制,其实对 PluginType 做拖展也能做到(选择自己喜欢的即可) Moya 初始化函数为: moya 接收一个 的插件数组初始化,并提供了基础的 log 插件 NetworkLoggerPlugin PluginType 生命周期函数为 核心调用顺序位置为, moya 实现了alamofire 的RequestInterceptor 数据加密请求,请求头的通用参数,我们可以通过插件形式实现 自定一个插件,实现 用插件实现,结构会非常的清晰 自定义一个 log 插件 注意一点,Moya 框架自带了一个 NetworkLoggerPlugin ,如果不想自定义的话,可以使用它,但是注意它接受一个 Configuration参数 这个参数里面有个Formatter,记得将 data-》string, 使用.prettyPrinted,这样打印出来的结果会好看点,我司使用的是自定义,为了区分打印 globalParam, 业务param等等,不过实现原理和 Moya 自带的差不多 从此插件开始,后续均为 APIPlugin 核心是,在 APIProvider 中,我们在执行插件 didEnd之前 APIResponseValidation去 validate APIResult 的业务,如果业务上有特殊需要,可以对特殊的 code,进行错误抛出,比如业务上code: 8888,尽管状态码200,但是我们仍然认为是不成功的一次请求,走的是failed,从而走插件 的 didEnd业务处理(Toast 啥的),而不会进入 success toast 我简单处理了下,根据自己的业务处理弹窗即可 提到 toast,这里再埋一个坑,toast 大家很常用,但是 toast 封装也很重要,如果有时间,我会抽出我司封装的 toast 组件,非常非常 nice! 稍微透露下

iOS Swift 修改状态栏的风格

最近在学习Swift的时候,重新梳理了一遍修改状态栏的知识点      1. 以UIAPPlication为准 设置 UIApplication.shared.statusBarStyle 的值来确定状态栏的风格,UIApplication.shared.statusBarStyle 为枚举:.default .lightContent(iOS 7.0) .darkContent(iOS 13.0) 前提是在plist.info文件中设置View controller-based status bar appearance 的值为false     2. 以控制器中重写父类的preferredStatusBarStyle属性的get方法为准 来设置状态栏的风格,返回值为 UIStatusBarStyle类型的枚举,枚举值分别是: .default  .lightContent(iOS 7.0) .darkContent(iOS 13.0) , 前提是在plist.info文件中设置 View controller-based status bar appearance 的值为true 以上dackContent的值为iOS13 暗黑风格新添加的属性. 如果不需要适配暗黑属性,可在info文件中设置 User Interface Style 的值为 Light 第一种方法 可以直接设置设置UIApplication.shared.statusBarStyle即可。 第二种方法,在父类变量preferredStatusBarStyle的get方法中 返回一个控制状态栏的变量,在需要改变风格的时候 改变控制状态栏变量的值,然后调用setNeedsStatusBarAppearanceUpdate()方法,这个方法调用之后 系统会自动再去读取preferredStatusBarStyle变量返回的值,即可设置状态栏的风格。     此时要注意,需要重写NavigationController的childForStatusBarStyle和childForStatusBarHidden方法,才能够在viewController中设置生效:

iOS中OC与Swift互相桥接混编(注意项目中有多个TARGETS的情况)

Swift项目中桥接OC文件相对来说比较简单,一般在第一次创建OC文件的时候Xcode会弹出创建桥接头文件的提示 直接点击Create Bridging Header系统就自动为我们创建好了桥接头文件,并且自动将头文件路径配置好了,所以这种方式非常便捷 但是,有时候我们并不是第一次在Swift项目中创建OC文件时,Xcode并不会再为我们自动创建桥接文件,需要我们自己创建,然后配置路径。 我们自己新建一个.h文件。命名为xxx-Bridging-Header.h就行了,也可以随意命名,创建好以后,在Build Settings中找到上图中的Objecttive-C Bridging Header选项,然后将创建好的head文件拖到这个路径配置中,就可以了 然后在创建的这个head文件中#import需要使用到的OC文件。就可以在Swift中索引出OC文件的类了。一般在OC项目中桥接Swift文件,只需要引入头文件#import "xxx-Swift.h"就可以在OC中调用Swift相关的类了。xxx为项目的Project Name 但是,在实际桥接中,常常会遇到报错’xxx-Swift.h’ file not found,头文件找不到的情况。 因为项目配置中桥接头文件的名称有可能和我们项目名称不一致,所以会导致桥接头找不到。 这个时候我们需要检查Build Setting中的Objecttive-C Generated Interface Header Name选项是否和我们import的名称一样,需要改成一样的就可以解决报错问题了。 如图,如果这个选项中的名称为test-Swift.h,那么我们在OC中桥接的时候就应该为#import "test-Swift.h",才能不报错。 如果遇到项目中有多个TARGETS的情况,我们在创建相应的OC/Swift文件的时候需要选中所有需要桥接的TARGETS选项,才能在对应的TARGETS的Build Setting中看到对应桥接路径配置的选项,然后每个TARGETS的Head都需要配置一样 不然在切换到不同的TARGETS下以后,也有可能因为不同TARGETS下配置的Head不一致,导致在不同的TARGETS下再次出现’xxx-Swift.h’ file not found的错误。

iOS Swift5.0 WKWebView使用JS与Swift交互

1.创建wkwebview

2.ViewController实现两个协议 两个协议分别是:WKNavigationDelegate WKScriptMessageHandler

WKNavigationDelegate:判断页面加载完成,只有在页面加载完成了,才能在swift中调webview中的js方法 WKScriptMessageHandler: 在webview中给swift发消息时要用到协议中的一个方法来接收 3.Swift调用WebView中的JS方法

4.拦截WebView中给Swift发消息

***隐藏网址***

iOS swift 懒加载的使用

记录一下swift的懒加载  OC回顾懒加载 - (UIButton *)but{ if (!_but){ _but = ; ; } return _but; } 下面来看swift怎么创建懒加载用到关键字lazy swift有两种加载方法 第一种方式 lazy var butt = UIButton() 第二种方式可以设置更多的属性 lazy var but:UIButton ={ let but =UIButton() but.setTitle("按钮", for: .normal) return but }() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. print(but) } 总结:用到的时候再加载,多次用到只加载一次,swift里用lazy关键字创建懒加载***隐藏网址***

iOS Swift 相机步骤与注意事项

第一步:初始化 AVCaptureDevicelet capDevice = AVCaptureDevice.default(for: .video) 第二步:初始化 设备会话管理器  管理输入输出 let  capDevicetureSession = AVCaptureSession.init()         ///设置        capDevicetureSession?.canSetSessionPreset(AVCaptureSession.Preset.init(rawValue: "AVCaptureSessionPreset1280x720"))        capDevicetureSession?.sessionPreset = AVCaptureSession.Preset.init(rawValue: "AVCaptureSessionPreset1280x720") 第三步:初始化相机设备输入流 do {             ///初始化输入流           let  capDeviceInput= try AVCaptureDeviceInput.init(device:capDevice!)             ///添加输入流             if capDevicetureSession?.canAddInput(capDeviceInput!) == true {                 capDevicetureSession?.addInput(capDeviceInput!)             }         } catch {             print("相机初始化失败")         } 第四步: 拍照 初始化照片输出流 ///初始化照片输出流        let  capDeviceImgOutput = AVCapturePhotoOutput.init()         ///添加输出流         if capDevicetureSession?.canAddOutput(capDeviceImgOutput!) == true {             capDevicetureSession?.addOutput(capDeviceImgOutput!)         } 第五步:录像 初始化视频输出流 ///初始化视频数据输出流         let capDeviceVideoOutput = AVCaptureVideoDataOutput.init()         ///添加输出流         if capDevicetureSession?.canAddOutput(capDeviceVideoOutput!) == true {             capDevicetureSession?.addOutput(capDeviceVideoOutput!)         }         capDeviceVideoOutput?.alwaysDiscardsLateVideoFrames = true //kCVPixelBufferPixelFormatTypeKey         capDeviceVideoOutput?.videoSettings =         capDeviceVideoOutput?.setSampleBufferDelegate( self , queue:DispatchQueue.init(label:"video")) 第六步:因为录像肯定还需要同步录制音频所以初始化音频的输入  输出 因为音频不能用上面的相机device所以 ///初始化麦克风设备         let audioDevice =AVCaptureDevice.default(for: .audio)         do {             // Wrap the audio device in a capture device input.             capDeviceAudioInput= try AVCaptureDeviceInput(device: audioDevice!)             // If the input can be added, add it to the session.             if capDevicetureSession!.canAddInput(capDeviceAudioInput!) {                 capDevicetureSession!.addInput(capDeviceAudioInput!)             }        } catch {             print("麦克风失败")         } ///初始化音频输出流        let   capDeviceAudioOutput=  AVCaptureAudioDataOutput.init()         if capDevicetureSession!.canAddOutput(capDeviceAudioOutput!){            capDevicetureSession!.addOutput(capDeviceAudioOutput!)         }         capDeviceAudioOutput?.setSampleBufferDelegate( self , queue:DispatchQueue.init(label:"audio")) 第七步:初始化视频连接器 let  videoConnection = capDeviceVideoOutput?.connection(with: .video)         videoConnection?.automaticallyAdjustsVideoMirroring = false         //设置视频输出方向         videoConnection?.videoOrientation = .portrait         //判断是否支撑视频稳定 可以显著提高视频的质量 只会在录制视频文件涉及到         if videoConnection!.isVideoStabilizationSupported {             videoConnection?.preferredVideoStabilizationMode = .auto         }第八步:最重要的一步:将所有的输入输出流都添加到设备会话管理器 前面几步已经添加了 最后就是初始化预览相机的视图了 ///初始化相机输出流预览图层         let capDevicePreViewLayer = AVCaptureVideoPreviewLayer.init(session: capDevicetureSession!)          capDeviceContentView=SYDIYCameraContentView.init(frame: preViewLayerFrame)          capDevicePreViewLayer?.frame=CGRect.init(x:0, y:0, width: preViewLayerFrame.size.width, height: preViewLayerFrame.size.height)          capDevicePreViewLayer?.videoGravity = .resizeAspectFill 然后开启会话管理器启动设备运行 capDevicetureSession?.startRunning() 注意事项就是:所有设置必须在会话管理器启动前初始化并且设置,不然拍照 和 录像 会有很多小问题

如果你还想了解更多这方面的信息,记得收藏关注本站。