iOS开发——系统API使用问题汇总(+解决方案)

平时实际开发中遇到最多的问题是什么?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
2
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero

3.UISlider无法拖动进度条的问题解决

在我听播放详情页的时候UISlider刚开始播放的时候距离左边20像素的位置之前的是不能拖动的,因为iOS在左边预留了20像素的手势返回处理。

当UISlider起始位置贴近屏幕边框的时候,UISilder不能拖动的原因是因为手势返回的原因,需要关闭手势返回就可以了。

1
2
3
- (BOOL)navigationControllerShouldDragback:(UINavigationController *)navigationController {
return false;
}

4.view被导航条给覆盖了,并没有从导航条的下面开始布局

解决方法:

(1)、设置edgesForExtendedLayout

1
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
2
在接收到通知的userInfo中,会包含一个AVAudioSessionInterruptionTypeKey,用来标识中断开始和中断结束.
当中断类型为AVAudioSessionInterruptionTypeKeyEnded时,userInfo中还会包含一个AVAudioSessionInterruptionOptions来表明音频会话是否已经重新激活以及是否可以再次播放

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
17
if 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
self.assetWriterVideoInput = ({
AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:({
// 录制参数。 根据调节参数实现压缩
@{
AVVideoCodecKey : AVVideoCodecH264,//编码方式
AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill,
AVVideoWidthKey : @(960),//视频的宽高
AVVideoHeightKey : @(540),
// 压缩参数 ,10 表示清晰度。
AVVideoCompressionPropertiesKey : ({
@{
AVVideoAverageBitRateKey : @(640 * 320 * 10),//压缩码率
AVVideoMaxKeyFrameIntervalKey : @(10),//清晰度
AVVideoProfileLevelKey : AVVideoProfileLevelH264HighAutoLevel//清晰度等级,枚举类型
};
})
};
})]
});
// yes指明输入应针对实时进行优化
writerInput.expectsMediaDataInRealTime = YES;
  • 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中。

曹理鹏(iCocos)-梦工厂

曹理鹏(iCocos)-梦工厂

坚持原创技术分享,您的支持将鼓励我继续创作!