Swift-截圖圖片指定區域, 並生成新圖片

擷取圖_1.png
專案中遇到了截圖指定區域圖片的功能, 比如一張全車圖, 使用者可以在手機上自由的畫圈, 畫完後要擷取到畫圈區域的圖片, 然後進行處理. 經過試驗, 現在將我做的 demo
和思路這這裡和大家分享下.
一.首先分析下思路
1.使用 UIImageView
顯示要被裁剪的圖片;
2.因為需要擷取圖片, 由於在 imageView
上直接擷取的話, 會由於圖片畫素的原因有誤差, 因為截圖是在 layer上
擷取, 是 cgImage
, 所以需要先獲得當前圖片區大小畫素的圖片;
3.因為為了讓使用者使用直觀,涉及到劃線, [注] UIImageView
不能劃線, 所以要建立一個圖片大小的 UIView
用於畫線;
4.根據畫線的區域可以獲取到最左右前後四個點, 從而獲取到一個區域矩形;
5.根據獲取到的畫線區域截圖圖片.
整體思路就是這樣的, 大家可以先理解下思路, 接下來詳細給大家講下實現.
二.建立需要的控制元件
fileprivate var img_Car: UIImageView! //車img fileprivate var view_GetImgBg: UIView! //圖片bg,用於擷取當前大小的image fileprivate var img_GetBg: UIImage! //獲取當前的圖片, 用於截圖, 這樣避免圖片畫素影響, 使用當前顯示的大小 fileprivate var view_Crop: LineDrawView! //專門用於畫線 因為img不能畫線 fileprivate var img_Crop: UIImageView! //根據花圈區域截的圖 fileprivate var array_TouchCrop = [CGPoint]() //畫圈點 相對於背景view的位置 fileprivate var array_TouchImage = [CGPoint]() //相對於車圖的位置
因為需要相對於圖片區域截圖, 所以要使用 frame
佈局, 不要使用自動佈局.
1.建立圖片的 UIImageView
和圖片大小的 UIView
獲取當前大小畫素的圖片
let width_Img = kScreenW / 375 * 175 let margin_LeftImg = (kScreenW - width_Img) / 2 view_GetImgBg = UIView() addSubview(view_GetImgBg) view_GetImgBg.frame = CGRect(x: margin_LeftImg, y:margin_TopMid, width: width_Img, height: kScreenH - margin_TopMid * 2) img_Car = UIImageView() img_Car.contentMode = .scaleToFill img_Car.isUserInteractionEnabled = true img_Car.frame = CGRect(x: 0, y: 0, width: view_GetImgBg.frame.size.width, height: view_GetImgBg.frame.size.height) view_GetImgBg.addSubview(img_Car)
2.獲取當前圖片大小畫素的圖片用於擷取圖片
//獲取當前顯示圖片大小的圖 func getCurrentFrameImage() -> UIImage { UIGraphicsBeginImageContext(view_GetImgBg.bounds.size) view_GetImgBg.layer.render(in: UIGraphicsGetCurrentContext()!) let imgae = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imgae! }
3.建立用於畫線的 UIView
view_Crop = LineDrawView(frame: .zero, arrayPath: array_TouchCrop) addSubview(view_Crop) view_Crop.frame = CGRect(x: 0, y: 0, width: kScreenW, height: kScreenH)
4.建立 UIImageView
用於顯示最後擷取的圖片
img_Crop = UIImageView() img_Crop.isHidden = true view_GetImgBg.addSubview(img_Crop)
三.用於畫線的 UIView
因為 UIImageView
不能畫線, 即使用 CGGraphics
核心繪畫,所以需要專門建立相同大小的背景 UIView
用於繪畫.
在 demo
中我封裝的一個 UIView
專門用於繪畫, 解耦了下.
class LineDrawView: UIView { var array_Path = [CGPoint]() override init(frame: CGRect) { super.init(frame: frame) } convenience init(frame: CGRect, arrayPath: [CGPoint]) { self.init(frame: frame) array_Path = arrayPath backgroundColor = UIColor.clear } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func draw(_ rect: CGRect) { super.draw(rect) //獲取繪圖上下文 guard let context = UIGraphicsGetCurrentContext() else{return} if array_Path.count == 0 {return} //建立並設定路徑 let pathRef: CGMutablePath = CGMutablePath() pathRef.move(to: array_Path[0]) pathRef.addLines(between: array_Path) //新增路徑到圖形上下文 context.addPath(pathRef) //設定筆觸的顏色和寬度 context.setStrokeColor(UIColor.red.cgColor) context.setLineWidth(4) //繪製路徑 context.strokePath() } }
四.在 Touch
中處理軌跡
因為是要獲取使用者畫線的區域, 所以在 touch
中獲取到使用者活動的點即可.
因為背景畫圈的大小和圖片的大小不一致, 所以專案中使用了兩個陣列來儲存相對於圖片和畫圈背景view的點.
1.兩個儲存點的陣列:
fileprivate var array_TouchCrop = [CGPoint]() //畫圈點 相對於背景view的位置 fileprivate var array_TouchImage = [CGPoint]() //相對於車圖的位置
2.每次 TouchBegin
請空陣列, 初始化資料和頁面
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { initCropImageStatus() } func initCropImageStatus() { array_TouchCrop.removeAll() array_TouchImage.removeAll() createPath() img_Car.isHidden = false img_Crop.isHidden = true }
3. TouchesMove
中使用陣列儲存移動的點
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { array_TouchImage.append(touches.first!.location(in: img_Car)) array_TouchCrop.append(touches.first!.location(in: view_Crop)) }
4.在畫圈結束後, 根據儲存的點計算區域, 進行截圖
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { if array_TouchCrop.count == 0{return} createPath() var topPoint: CGPoint = array_TouchImage[0] var righttPoint: CGPoint = array_TouchImage[0] var bottomPoint: CGPoint = array_TouchImage[0] var leftPoint: CGPoint = array_TouchImage[0] for item in array_TouchImage{ if item.y < topPoint.y{ topPoint = item } if item.x > righttPoint.x{ righttPoint = item } if item.y > bottomPoint.y{ bottomPoint = item } if item.x < leftPoint.x{ leftPoint = item } } //設定最小的畫圈範圍, 小於10則不進行處理 if bottomPoint.y - topPoint.y <= 10{ return } if righttPoint.x - leftPoint.x <= 10{ return } let frame = CGRect(x: leftPoint.x, y: topPoint.y, width: righttPoint.x - leftPoint.x, height: bottomPoint.y - topPoint.y) img_Crop.frame = frame img_Crop.image = clipWithImageRect(clipFrame: frame, bgImage: img_GetBg) img_Car.isHidden = true img_Crop.isHidden = false if let block = getImage{ block(img_Crop.image ?? UIImage.init()) } }
五.繪畫及擷取圖片
1.根據儲存的畫圈點進行繪製, 繪製其實就是調取 UIView
的 drawRect
方法進行重繪.
func createPath() { view_Crop.array_Path = array_TouchCrop view_Crop.setNeedsDisplay() //重繪 }
2.根據計算的區域 frame
和用於裁剪的圖片來進行裁剪:
func clipWithImageRect(clipFrame: CGRect, bgImage: UIImage) -> UIImage { let rect_Scale = CGRect(x: clipFrame.origin.x, y: clipFrame.origin.y, width: clipFrame.size.width, height: clipFrame.size.height) let cgImageCorpped = bgImage.cgImage?.cropping(to: rect_Scale) let img_Clip = UIImage.init(cgImage: cgImageCorpped!, scale: 1, orientation: UIImageOrientation.up) return img_Clip }
到這裡就大功告成了, 因為我為了解耦, 將這個過程封裝在一個 UIView
裡面, 方便使用, 結束了可以通過代理,閉包等方式傳出去, 此 demo
採用了閉包的形式, 截圖的圖片有了, 就可以處理了.
詳細的程式碼可以到github下載: iOSer/YTCutDrawImageDemo" target="_blank" rel="nofollow,noindex">Github下載地址