共计 3630 个字符,预计需要花费 10 分钟才能阅读完成。
拍照,用零碎的,简略
本文次要是讲,抉择区域的旋转
- 照片的旋转,用仿射变换
- 抉择区域的旋转,就是旋转坐标系中的点,有一个坐标变换
手机 UIKit 框架下,view 的坐标系原点,是 view 的左上角
旋转坐标系中的点,个别坐标系用核心,不便
拍照,略。开始旋转抉择区域
1,摆放好
下面有两个视图,放照片的 UIImageView, 和照片上拖拽视图 sketch
布局没有采纳,束缚
因为旋转的时候,他们的 frame 变来变去,
间接手动计算 frame,更加的直观
if let img = image{let s = img.size.size(in: measure.s)
imgView.frame.size = s
imgView.center = measure.center
sketch.frame = imgView.frame
// 照片
view.addSubview(imgView)
// 照片上的,拖拽区域
view.addSubview(sketch)
// ...
}
零碎拍照,默认分辨率,4:3
为了简化计算,这里 imageView 的高 / 宽 = 其 image 的高 / 宽
拍照的图片,很大,适合的放在指定的小区域中
extension CGSize{func size(in std: CGSize) -> CGSize{
let hRatio = height / std.height
let wRatio = width / std.width
let reSolution = height / width
let s: CGSize
if hRatio > wRatio{
// 图片更窄
s = CGSize(width: std.height / reSolution, height: std.height)
}
else{
// 图片更宽
s = CGSize(width: std.width, height: std.width * reSolution)
}
return s
}
}
2,旋转
从图 a 到图 b , 是一个右旋
- 旋转抉择区域视图,为了简化计算,旋转抉择区域的 frame 与看到的图片贴合
替换抉择区域的宽和高,就是批改尺寸 size,
复原其原来的中心点 center , 就是更新其原点
- 旋转抉择区域视图,上的四个拖拽点,
先在原来的坐标系中,旋转角度,
抉择区域视图的原点变了,四个拖拽点旋转角度后,还需一个平移的弥补
// 记录以后的角度
var angle: CGFloat = 0
@objc func rightTurn(){rotate(with: .rhs)
}
func rotate(with direction: RotateOpt) {
let sizeOld = sketch.frame.size
let originOld = sketch.frame.origin
let center = sketch.center
switch direction {
case .lhs:
// 逆时针
angle -= 1
sketch.defaultPoints.update(clockwize: false, by: sizeOld)
case .rhs:
// 顺时针
angle += 1
// 旋转四个点
sketch.defaultPoints.update(clockwize: true, by: sizeOld)
}
// 图片视图,间接仿射变换
imgView.transform = CGAffineTransform(rotationAngle: ImgSingleAngle.time * angle)
sketch.frame.size = CGSize(width: sizeOld.height, height: sizeOld.width)
sketch.center = center
let originNew = sketch.frame.origin
// 再弥补四个点
sketch.defaultPoints.patch(vector: originNew - originOld)
// 更新绘制
sketch.reloadData()}
旋转点的坐标,通过角度的变动,计算
// 拿 size, 去算中心点
mutating
func update(clockwize beC: Bool, by area: CGSize){
let lhsTop: CGPoint, rhsTop: CGPoint, rhsBottom: CGPoint, lhsBottom: CGPoint
let center = CGPoint(x: area.width / 2, y: area.height / 2)
if beC{lhsTop = clockwize(rightTurn: leftTop, forCoordinate: center)
// ...
}
else{lhsTop = antiClockwize(leftTurn: leftTop, forCoordinate: center)
// ...
}
leftTop = lhsTop
// ...
}
// 顺时针旋转
private
func clockwize(rightTurn target: CGPoint, forCoordinate origin: CGPoint) -> CGPoint {
let dx = target.x - origin.x
let dy = target.y - origin.y
let radius = sqrt(dx * dx + dy * dy)
let azimuth = atan2(dy, dx) // in radians
let x = origin.x - radius * sin(azimuth)
let y = origin.y + radius * cos(azimuth)
return CGPoint(x: x, y: y)
}
3,旋转增强,为了更好的利用手机区域,图片竖着更大,横着稍小一些
图片视图,仿射变换,是旋转 rotate + 缩放 scale
抉择区域视图 sketch, 不能简略的替换宽和高,
稍麻烦了一些
抉择区域视图 sketch 的 frame, 中心点不变,他的 size 有两个抉择,切换就好了
在上一步的根底上,计算抉择区域视图 sketch 的四个坐标
- 竖着的,转横着的,大变小,上一步的坐标,放大就行
- 横着的,转竖着的,小变大,先放大,再用规范坐标(竖着的尺寸,替换后),投入计算
func rotate(with direction: RotateOpt) {
guard let img = image else {return}
let imgRatio = img.size.height / img.size.width
let const = 4.0/3
print(imgRatio, const)
let sizeOld = sketch.frame.size
let originOld = sketch.frame.origin
let center = sketch.center
let bigS = img.size.size(in: measure.s)
let clockwize: Bool
switch direction {
case .lhs:
// 逆时针
angle -= 1
clockwize = false
case .rhs:
// 顺时针
angle += 1
clockwize = true
// 下一步,对 UI 的批改,影响上一步
}
var ratio: CGFloat = 1
let smallS = img.size.size(by: measure.horizontal)
var imgTransform = CGAffineTransform(rotationAngle: ImgSingleAngle.time * angle)
if Int(angle) % 2 == 1{
ratio = smallS.width / bigS.height
imgTransform = imgTransform.scaledBy(x: ratio, y: ratio)
sketch.frame.size = smallS
}
else{
ratio = bigS.height / smallS.width
sketch.frame.size = bigS
}
// 旋转四个拖拽的点之前,先复位
// 小的,变大的,的时候,须要操作
if Int(angle) % 2 == 0{sketch.defaultPoints.scale(r: ratio, forS: sizeOld)
}
// 旋转,和平移弥补之前,先化为标注的,也就是竖着的尺寸
sketch.defaultPoints.update(clockwize: clockwize, by: sizeOld)
imgView.transform = imgTransform
sketch.center = center
let originNew = sketch.frame.origin
// 弥补,跟上一步一样
sketch.defaultPoints.patch(vector: originNew - originOld)
// 四个拖拽的点,属于失常规范的图片
// 横着摆放,大变小
if Int(angle) % 2 == 1{sketch.defaultPoints.scale(r: ratio, forS: sizeOld)
}
// 绘制
sketch.reloadData()}
前文,低仿扫描全能王的抉择区域性能
github repo
正文完