平时实际开发中遇到最多的问题是什么?Bug?技术实现?技术难点?接口使用?方法优化?界面调整? 其实最后总结来说终究是关于Api的问题,因为不管怎么样最后总会转成苹果对应的Api或者底层与处理。那么我们在使用苹果系统API的时候会遇到一些什么问题呢?也许你会说查文档,但是如果文档也没有提到相关的解决方案呢?那就懵逼了……
1. 解决添加tap手势后点击cell无响应
实现UIGestureRecognizerDelegate协议下面的方法1
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool { if NSStringFromClass(touch.view!.classForCoder) == "UITableViewCellContentView"{ return false } return true }
2.cell分割线不留空格
1 | cell.preservesSuperviewLayoutMargins = false |
3.UISlider无法拖动进度条的问题解决
在我听播放详情页的时候UISlider刚开始播放的时候距离左边20像素的位置之前的是不能拖动的,因为iOS在左边预留了20像素的手势返回处理。
当UISlider起始位置贴近屏幕边框的时候,UISilder不能拖动的原因是因为手势返回的原因,需要关闭手势返回就可以了。1
2
3- (BOOL)navigationControllerShouldDragback:(UINavigationController *)navigationController {
return false;
}
4.view被导航条给覆盖了,并没有从导航条的下面开始布局
解决方法:
(1)、设置edgesForExtendedLayout1
self.edgesForExtendedLayout = UIRectEdgeNone; //view不需要拓展到整个屏幕
(2)、设置导航条的透明度1
self.navigationController.navigationBar.translucent = NO;
5.我听设置当音频被占用的时候(如电话进来)系统会自动暂停当前使用的音频,如果想要系统使用完毕后恢复自己的音频使用 需要 播放自己的音频前
设置:
1 | [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; |
然后设置系统音频中断恢复的通知
1 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; |
中断发生时,应用程序的AVAudioSession会发送通知AVAudioSessionInterruptionNotification,注册通知代码如下:
1 | 在接收到通知的userInfo中,会包含一个AVAudioSessionInterruptionTypeKey,用来标识中断开始和中断结束. |
6.跟xib一起走过的坑
如果在xib中有一个控件, 已经明确设置尺寸了,输出的frame也是对的, 但是显示出来的效果不一样(比如尺寸变大了), 如果是这种情况一般就是autoresizingMask自动伸缩属性在搞鬼! 解决办法如下:1
2
3
4//xib的awakeFromNib方法中设置UIViewAutoresizingNone进行清空
- (void)awakeFromNib {
self.autoresizingMask = UIViewAutoresizingNone;
}
或者在layoutSubView中重新设置contentView的frame
7.一生部分对于系统视频音频API的使用较多,这里把我记得的坑公示,防止以后大家踩坑。
- 1.系统初始化AVCaptureSession的时候不能直接调用startSession的方法,需要先调用stopSession方法停止当前session,然后再调用startSession方法。否则会出现卡死主线程的问题。
(这里如果不手动显示调用stopSession,那么系统会利用主线程去停止session,而停止session是一个耗时操作,就会卡死主线程,出现页面卡死的现
- 2.因为之前一生拍摄需要拍照和视频录制相互切换,但是他们用的同一个session,只是视频的session中多了音频输入设备,如果直接使用session会造成录制的视频没有声音的问题(原因:因为session初始化的时候里面的视频设备音频设备还是使用的原来的session的。从拍照到录像的时候session中是没有音频设备的,所以需要重新加入音频设备,
注意(重点): 不能直接只加一个音频设备,需要把session中之前的输入输出设备全部移除掉,然后再重新加入新的输入输出音频视频设备,否则session不会生效。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 session.beginConfiguration()
//移除输入
if let input = self.getAVCaptureDeviceInput() {
let viewLayer = self.captureImage.layer
viewLayer.masksToBounds = true
let bounds = self.view.bounds
self.captureVideoPreviewLayer?.frame = bounds
for input in session.inputs {
if let input = input as? AVCaptureDeviceInput {
session.removeInput(input)
}
}
if session.canAddInput(input) {
session.addInput(input)
}
//移除输出
self.stillImageOutput = AVCaptureStillImageOutput()
let outputSettings = [AVVideoCodecKey : AVVideoCodecJPEG]
self.stillImageOutput?.outputSettings = outputSettings
for output in session.outputs {
if let output = output as? AVCaptureOutput {
session.removeOutput(output)
}
}
if session.canAddOutput(stillImageOutput) {
session.addOutput(self.stillImageOutput)
}
session.commitConfiguration()
- 3.视频拍摄设备方向和播放视频的设备方向是不一致的。所以需要在预览视频和上传后播放视频做视频的方向旋转处理。前置头像头和后置摄像头的处理方向是不一样的。
具体代码如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17if FrontCamera {
if self.videoFirstOrientation == .landscapeRight{
self.playerLayer?.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(Double.pi*0.5)))
}else if self.videoFirstOrientation == . landscapeLeft{
self.playerLayer?.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(Double.pi*1.5)))
} else if self.videoFirstOrientation == .portraitUpsideDown{
self.playerLayer?.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(Double.pi )))
}
} else {
if self.videoFirstOrientation == .landscapeLeft {
self.playerLayer?.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(Double.pi * 1.5)))
} else if self.videoFirstOrientation == .landscapeRight {
self.playerLayer?.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2)))
} else if self.videoFirstOrientation == .portraitUpsideDown {
self.playerLayer?.setAffineTransform(CGAffineTransform(rotationAngle: 0))
}
}
- 4.如果后期需要做滤镜或者实时视频滤镜,在DWVideoRecoder 中的
1
2
3- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
}
这个方法是拿到当前视频输入设备捕捉的每一帧画面,然后进行对每一帧画面进行处理。
这个方法中拿到当前帧的画面然后进行滤镜渲染显示
- 5.获取沙盒中的视频路径要用 url.path.
不能直接转换为String,否则拿到的地址是不对的。1
try? FileManager.default.removeItem(atPath: url.path)
- 6.视频的压缩主要取决于码率和关键帧率。
1 | self.assetWriterVideoInput = ({ |
- 7.目前没有做多音频视频合成的需求,如果有需求,可以在TakeViedeoController.swift中的mergeAndExportVideos方法中代码就是对多段音频视频合成处理的代码。
录制视频关键流程梳理
- 1.初始化session,在session中设置音频视频的输入输出设备。在DWVideoRecoder中
setupSessionInputs 和 setupSessionOutputs中 - 2.初始化摄像头,开启session,显示当前摄像头捕捉画面,为了防止按钮在白光下面显示不清晰,在view上加了一层蒙版。
- 3.开始录制调用DWRecorder中的startRecordWithSwift,然后往沙盒中写入视频数据。
- 4.结束录制后停止session,显示录制的最后一帧图像。调用session的stopSession方法,并且将.mov的文件转换为mp4格式视频保存到沙盒,然后删除之前的mov文件
- 5.重新录制视频只需要把页面按钮状态重置,停止session重新开始session。在TakeVideoViewController中直接调用重录方法cancelAction即可重录。
- 6.播放视频。从沙盒中获取之前录制的mp4文件路径放到当前的player中。