拍照,用系统的,简单
本文主要是讲,选择区域的旋转
- 照片的旋转,用仿射变换
- 选择区域的旋转,就是旋转坐标系中的点,有一个坐标变换
手机 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()
}