iOS白板踩坑
\
1.缩放手势
问题:
用手势实现的时候,需要每次捏合结束的scale 和 下次开始捏和的scale容易对不上,而且快速捏和结束的时候容易,scale容易越过自己设置的最大最小边界值。
避坑策略:
采用背景View是scrollview,通过Scrollview的代理方法,实现子view的捏和,捏和结束后重新计算子view的停留center
代码如下:
extension WHWhiteboardViewController {
func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
if displayToolView ?? false {
return nil
} else {
return drawV!
}
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
refreshImageContainerViewCenter()
}
func refreshImageContainerViewCenter() {
guard let scrollView = scrollView else {
return
}
let offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width) ? ((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5) : 0.0
let offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height) ? ((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5) : 0.0
let center = CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX,
y: scrollView.contentSize.height * 0.5 + offsetY)
drawV!.center = center
}
}
2.画布缩放
缩放经常改变画布的大小,所以不能用masonry实现,只能用frame值给捏和的view设值
3.图形擦除
问题:
图形擦除的时候,只找点所在的范围,因为映射造成擦除对称图形,
避坑策略
:需要确定图形所在的点的范围
代码如下:
/// 橡皮擦
func newEraserWithFlag(currentP: CGPoint, graphs: [String], gesture: UIGestureRecognizer) {
for graphID in graphs {
let layer: WHDrawingLayer? = self.layerDic[graphID]
guard let layer = layer else {
continue
}
/// 遍历图形上的所有点,得到图形点所在的范围
let points: [CGPoint] = layer.pointA
var currentPoint = points[0]
var maxX = currentPoint.x
var maxY = currentPoint.y
var minX = currentPoint.x
var minY = currentPoint.y
layer.pointA.forEach { (value) in
currentPoint = value
minX = ( minX >= currentPoint.x ) ? currentPoint.x : minX
minY = (minY >= currentPoint.y ) ? currentPoint.y : minY
maxX = (maxX <= currentPoint.x) ? currentPoint.x : maxX
maxY = (maxY <= currentPoint.y) ? currentPoint.y : maxY
}
if points.count == 0 {
continue
}
for var i in 0 ..< points.count - 1 {
let isContant = checkPointIsInLineByTwoPoints(point: currentPoint, charp0: points[i], charp1: points[i + 1] )
i += 1
let constant: CGFloat = 10
let contain = (currentP.x < maxX + constant && currentP.x > minX - constant) && (currentP.y < maxY + constant && currentP.y > minY - constant)
if isContant && contain {
CSTMeetingSDK.shareMeetingSDK.whiteboardService?.deleteGraph(graphId: graphID)
if self.layerDic.values.contains(layer) {
self.deleteGraph(graphid: graphID)
if gesture.isKind(of: UIPanGestureRecognizer.self) {
continue
} else {
return
}
}
}
}
}
}
\
/**
*判断点*point*是否在*charp0* 和 *charp1*两点构成的线段上
*/
func checkPointIsInLineByTwoPoints(point: CGPoint, charp0: CGPoint, charp1: CGPoint) -> Bool {
var contant = false
// 最大允许范围值 点到线段的最短距离小于该值说明在线段上
let maxAllowOffsetLength = 15.0
// 通过直线方程两点式计算一般式 ABC 函数 Ax+By+C = 0
let chara = charp1.y - charp0.y
let charb = charp0.x - charp1.x
let charc = charp1.x * charp0.y - charp0.x * charp1.y
// 带入点到直线距离公式求出距离 dis
var dis = abs((chara * point.x + charb * point.y + charc) / sqrt(pow(chara, 2) + pow(charb, 2)))
// 如果距离大于最大允许值 则说明不在线段上
if Double(dis) > maxAllowOffsetLength {
contant = false
} else {
// 判断投影点是否在线段上, 根据公式求出投影点的X 坐标 cornerX
let chard = (chara * point.y - charb * point.x)
let cornerX = -(chara * charc + charb * chard) / (pow(charb, 2) + pow(chara, 2))
// 判断cornerX 是否在线段上, t 如果在0~1之间说明在线段上,大于1 则 说明不在线段上,且考经断点charp1, 小于0 则不在线段上,且考经短短charp0 线性插值思想
let chart = (cornerX - charp0.x) / (charp1.x - charp0.x)
if chart > 3 {
// 最小距离是到charp1的距离
dis = distanceByTwoPoints(charp1: charp1, charp2: point)
} else if chart < 0 {
// 最小距离为到p2的距离
dis = distanceByTwoPoints(charp1: charp0, charp2: point)
}
// 再次判断真正的最小距离是否小于允许值,小于则该点在直线上, 反之则不在
if Double(dis) < maxAllowOffsetLength {
contant = true
} else {
contant = false
}
}
return contant
}
\
4.三阶贝塞尔
问题:
多端图形不统一或多点或缺点,涂鸦容易造成夹角
避坑策略:
因为多端同屏绘制现实图形,所以交互的点集必须一致,需要绘制方案统一,采取三阶贝塞尔曲线,不足三阶或多余的不够完成三阶段的点需直接连接绘制
代码如下:
/// 三阶贝塞尔尾点处理
func drawOthersEndPointWithThreeBezier() {
if self.validPoints.count > 4 {
let left = (self.validPoints.count - 4) % 3
if left != 0 {
let leftIndex = self.validPoints.count - left
let endIndex = self.validPoints.count
let tempArray = self.validPoints[leftIndex ..< endIndex]
for i in leftIndex ..< endIndex {
drawEndPoint(currentPoint: tempArray[i])
}
}
} else {
for temPoint in self.validPoints {
drawEndPoint(currentPoint: temPoint)
}
}
ctr = 0
}
/// 三阶贝塞尔UIBezierPath
func drawEndPointWithThreeBezier(currentPoint: CGPoint) {
ctr+=1
pts[ctr] = currentPoint
if ctr == 4 {
pts[3] = CGPoint(x: (pts[2].x + pts[4].x)/2, y: (pts[2].y + pts[4].y)/2)
drawEndPoint(currentPoint: pts[3], controlPoint1: pts[1], controlPoint2: pts[2])
pts[0] = pts[3]
pts[1] = pts[4]
ctr = 1
}
}
\
\
5 手势冲突,
问题:
父视图中实现了touchBegin的触摸屏幕手势相关功能,导致白板子视图中touchbegin手势响应不了
避坑策略:
该用手势uigesturepan实现白板涂鸦绘制功能