SwiftUI圖片縮放、拼圖等處理教程
前言
采用SwiftUI Core Graphics技術(shù),與C#的GDI+繪圖類似,具體概念不多說,畢竟我也是新手,本文主要展示效果圖及代碼,本文示例代碼需要請拉到文末自取。
1、圖片縮放
- 完全填充,變形壓縮
- 將圖像居中縮放截取
- 等比縮放
上面三個效果,放一起比較好對比,如下
原圖 - 完全填充,變形壓縮 - 居中縮放截取 - 等比縮放
- 第1張為原圖
- 第2張為完全填充,變形壓縮
- 第3張為圖像居中縮放截取
- 第4張為等比縮放
示例中縮放前后的圖片可導(dǎo)出
2、圖片拼圖
顧名思義,將多張圖片組合成一張圖,以下為多張美圖原圖:
多張美圖原圖
選擇后,界面中預(yù)覽:
界面中預(yù)覽
導(dǎo)出拼圖查看效果:
導(dǎo)出拼圖
3、圖片操作方法
最后上圖片縮放、拼圖代碼:
import SwiftUI struct ImageHelper { static let shared = ImageHelper() private init() {} // NSView 轉(zhuǎn) NSImage func imageFromView(cview: NSView) -> NSImage? { // 從view、data、CGImage獲取BitmapImageRep // NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:data]; // NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:CGImage]; guard let bitmap: NSBitmapImageRep = cview.bitmapImageRepForCachingDisplay(in: cview.visibleRect) else { return nil } cview.cacheDisplay(in: cview.visibleRect, to: bitmap) let image: NSImage = NSImage(size: cview.frame.size) image.addRepresentation(bitmap) return image; } // 保存圖片到本地 func saveImage(image: NSImage, fileName: String) -> Bool { guard var imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) else { return false } // [imageRep setSize:size]; // 只是打開圖片時的初始大小,對圖片本省沒有影響 // jpg if(fileName.hasSuffix("jpg")) { let quality:NSNumber = 0.85 // 壓縮率 imageData = imageRep.representation(using: .jpeg, properties:[.compressionFactor:quality])! } else { // png imageData = imageRep.representation(using: .png, properties:[:])! } do { // 寫文件 保存到本地需要關(guān)閉沙盒 ---- 保存的文件路徑一定要是絕對路徑,相對路徑不行 try imageData.write(to: URL(fileURLWithPath: fileName), options: .atomic) return true } catch { return false } } // 將圖片按照比例壓縮 // rate 壓縮比0.1~1.0之間 func compressedImageDataWithImg(image: NSImage, rate: CGFloat) -> NSData? { guard let imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) else { return nil } guard let data: Data = imageRep.representation(using: .jpeg, properties:[.compressionFactor:rate]) else { return nil } return data as NSData; } // 完全填充,變形壓縮 func resizeImage(sourceImage: NSImage, forSize size: NSSize) -> NSImage { let targetFrame: NSRect = NSMakeRect(0, 0, size.width, size.height); let sourceImageRep: NSImageRep = sourceImage.bestRepresentation(for: targetFrame, context: nil, hints: nil)! let targetImage: NSImage = NSImage(size: size) targetImage.lockFocus() sourceImageRep.draw(in: targetFrame) targetImage.unlockFocus() return targetImage; } // 將圖像居中縮放截取targetsize func resizeImage1(sourceImage: NSImage, forSize targetSize: CGSize) -> NSImage { let imageSize: CGSize = sourceImage.size let width: CGFloat = imageSize.width let height: CGFloat = imageSize.height let targetWidth: CGFloat = targetSize.width let targetHeight: CGFloat = targetSize.height var scaleFactor: CGFloat = 0.0 let widthFactor: CGFloat = targetWidth / width let heightFactor: CGFloat = targetHeight / height scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor // 需要讀取的源圖像的高度或?qū)挾? let readHeight: CGFloat = targetHeight / scaleFactor let readWidth: CGFloat = targetWidth / scaleFactor let readPoint: CGPoint = CGPoint(x: widthFactor > heightFactor ? 0 : (width - readWidth) * 0.5, y: widthFactor < heightFactor ? 0 : (height - readHeight) * 0.5) let newImage: NSImage = NSImage(size: targetSize) let thumbnailRect: CGRect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height) let imageRect: NSRect = NSRect(x: readPoint.x, y: readPoint.y, width: readWidth, height: readHeight) newImage.lockFocus() sourceImage.draw(in: thumbnailRect, from: imageRect, operation: .copy, fraction: 1.0) newImage.unlockFocus() return newImage; } // 等比縮放 func resizeImage2(sourceImage: NSImage, forSize targetSize: CGSize) -> NSImage { let imageSize: CGSize = sourceImage.size let width: CGFloat = imageSize.width let height: CGFloat = imageSize.height let targetWidth: CGFloat = targetSize.width let targetHeight: CGFloat = targetSize.height var scaleFactor: CGFloat = 0.0 var scaledWidth: CGFloat = targetWidth var scaledHeight: CGFloat = targetHeight var thumbnailPoint: CGPoint = CGPoint(x: 0.0, y: 0.0) if __CGSizeEqualToSize(imageSize, targetSize) == false { let widthFactor: CGFloat = targetWidth / width let heightFactor: CGFloat = targetHeight / height // scale to fit the longer scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor scaledWidth = ceil(width * scaleFactor) scaledHeight = ceil(height * scaleFactor) // center the image if (widthFactor > heightFactor) { thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5 } else if (widthFactor < heightFactor) { thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5 } } let newImage: NSImage = NSImage(size: NSSize(width: scaledWidth, height: scaledHeight)) let thumbnailRect: CGRect = CGRect(x: thumbnailPoint.x, y: thumbnailPoint.y, width: scaledWidth, height: scaledHeight) let imageRect: NSRect = NSRect(x: 0.0, y:0.0, width: width, height: height) newImage.lockFocus() sourceImage.draw(in: thumbnailRect, from: imageRect, operation: .copy, fraction: 1.0) newImage.unlockFocus() return newImage; } // 將圖片壓縮到指定大?。↘B) func compressImgData(imgData: NSData, toAimKB aimKB: NSInteger) -> NSData? { let aimRate: CGFloat = CGFloat(aimKB * 1000) / CGFloat(imgData.length) let imageRep: NSBitmapImageRep = NSBitmapImageRep(data: imgData as Data)! guard let data: Data = imageRep.representation(using: .jpeg, properties:[.compressionFactor:aimRate]) else { return nil } print("數(shù)據(jù)最終大?。篭(CGFloat(data.count) / 1000), 壓縮比率:\(CGFloat(data.count) / CGFloat(imgData.length))") return data as NSData } // 組合圖片 func jointedImageWithImages(imgArray: [NSImage]) -> NSImage { var imgW: CGFloat = 0 var imgH: CGFloat = 0 for img in imgArray { imgW += img.size.width; if (imgH < img.size.height) { imgH = img.size.height; } } print("size : \(NSStringFromSize(NSSize(width: imgW, height: imgH)))") let togetherImg: NSImage = NSImage(size: NSSize(width: imgW, height: imgH)) togetherImg.lockFocus() let imgContext: CGContext? = NSGraphicsContext.current?.cgContext var imgX: CGFloat = 0 for imgItem in imgArray { if let img = imgItem as? NSImage { let imageRef: CGImage = self.getCGImageRefFromNSImage(image: img)! imgContext?.draw(imageRef, in: NSRect(x: imgX, y: 0, width: img.size.width, height: img.size.height)) imgX += img.size.width; } } togetherImg.unlockFocus() return togetherImg; } // NSImage轉(zhuǎn)CGImageRef func getCGImageRefFromNSImage(image: NSImage) -> CGImage? { let imageData: NSData? = image.tiffRepresentation as NSData? var imageRef: CGImage? = nil if(imageData != nil) { let imageSource: CGImageSource = CGImageSourceCreateWithData(imageData! as CFData, nil)! imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) } return imageRef; } // CGImage 轉(zhuǎn) NSImage func getNSImageWithCGImageRef(imageRef: CGImage) -> NSImage? { return NSImage(cgImage: imageRef, size: NSSize(width: imageRef.width, height: imageRef.height)) // var imageRect: NSRect = NSRect(x: 0, y: 0, width: 0, height: 0) // // var imageContext: CGContext? = nil // var newImage: NSImage? = nil // // imageRect.size.height = CGFloat(imageRef.height) // imageRect.size.width = CGFloat(imageRef.width) // // // Create a new image to receive the Quartz image data. // newImage = NSImage(size: imageRect.size) // // newImage?.lockFocus() // // Get the Quartz context and draw. // imageContext = NSGraphicsContext.current?.cgContext // imageContext?.draw(imageRef, in: imageRect) // newImage?.unlockFocus() // // return newImage; } // NSImage轉(zhuǎn)CIImage func getCIImageWithNSImage(image: NSImage) -> CIImage?{ // convert NSImage to bitmap guard let imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) else { return nil } // create CIImage from imageRep let ciImage: CIImage = CIImage(bitmapImageRep: imageRep)! // create affine transform to flip CIImage let affineTransform: NSAffineTransform = NSAffineTransform() affineTransform.translateX(by: 0, yBy: 128) affineTransform.scaleX(by: 1, yBy: -1) // create CIFilter with embedded affine transform let transform:CIFilter = CIFilter(name: "CIAffineTransform")! transform.setValue(ciImage, forKey: "inputImage") transform.setValue(affineTransform, forKey: "inputTransform") // get the new CIImage, flipped and ready to serve let result: CIImage? = transform.value(forKey: "outputImage") as? CIImage return result; } }
4、示例代碼
界面布局及效果展示代碼
import SwiftUI struct TestImageDemo: View { @State private var sourceImagePath: String? @State private var sourceImage: NSImage? @State private var sourceImageWidth: CGFloat = 0 @State private var sourceImageHeight: CGFloat = 0 @State private var resizeImage: NSImage? @State private var resizeImageWidth: String = "250" @State private var resizeImageHeight: String = "250" @State private var resize1Image: NSImage? @State private var resize1ImageWidth: String = "250" @State private var resize1ImageHeight: String = "250" @State private var resize2Image: NSImage? @State private var resize2ImageWidth: String = "250" @State private var resize2ImageHeight: String = "250" @State private var joinImage: NSImage? var body: some View { GeometryReader { reader in VStack { HStack { Button("選擇展示圖片縮放", action: self.choiceResizeImage) Button("選擇展示圖片拼圖", action: self.choiceJoinImage) Spacer() } HStack { VStack { if let sImage = sourceImage { Section(header: Text("原圖")) { Image(nsImage: sImage) .resizable().aspectRatio(contentMode: .fit) .frame(width: reader.size.width / 2) Text("\(self.sourceImageWidth)*\(self.sourceImageHeight)") Button("導(dǎo)出", action: { self.saveImage(image: sImage) }) } } if let sImage = self.joinImage { Section(header: Text("拼圖")) { Image(nsImage: sImage) .resizable().aspectRatio(contentMode: .fit) .frame(width: reader.size.width) Button("導(dǎo)出", action: { self.saveImage(image: sImage) }) } } } VStack { Section(header: Text("完全填充,變形壓縮")) { VStack { Section(header: Text("Width:")) { TextField("Width", text: self.$resizeImageWidth) } Section(header: Text("Height:")) { TextField("Height", text: self.$resizeImageHeight) } if let sImage = resizeImage { Image(nsImage: sImage) Text("\(self.resizeImageWidth)*\(self.resizeImageHeight)") Button("導(dǎo)出", action: { self.saveImage(image: sImage) }) } } } } VStack { Section(header: Text("將圖像居中縮放截取")) { VStack { Section(header: Text("Width:")) { TextField("Width", text: self.$resize1ImageWidth) } Section(header: Text("Height:")) { TextField("Height", text: self.$resize1ImageHeight) } if let sImage = resize1Image { Image(nsImage: sImage) Text("\(self.resize1ImageWidth)*\(self.resize1ImageHeight)") Button("導(dǎo)出", action: { self.saveImage(image: sImage) }) } } } } VStack { Section(header: Text("等比縮放")) { VStack { Section(header: Text("Width:")) { TextField("Width", text: self.$resize2ImageWidth) } Section(header: Text("Height:")) { TextField("Height", text: self.$resize2ImageHeight) } if let sImage = resize2Image { Image(nsImage: sImage) Text("\(self.resize2ImageWidth)*\(self.resize2ImageHeight)") Button("導(dǎo)出", action: { self.saveImage(image: sImage) }) } } } } Spacer() } Spacer() } } } private func choiceResizeImage() { let result: (fail: Bool, url: [URL?]?) = DialogProvider.shared.showOpenFileDialog(title: "", prompt: "", message: "選擇圖片", directoryURL: URL(fileURLWithPath: ""), allowedFileTypes: ["png", "jpg", "jpeg"]) if result.fail { return } if let urls = result.url, let url = urls[0] { self.sourceImagePath = url.path self.sourceImage = NSImage(contentsOf: URL(fileURLWithPath: self.sourceImagePath!)) self.sourceImageWidth = (self.sourceImage?.size.width)! self.sourceImageHeight = (self.sourceImage?.size.height)! if let resizeWidth = Int(self.resizeImageWidth), let resizeHeight = Int(self.resizeImageHeight) { self.resizeImage = ImageHelper.shared.resizeImage(sourceImage: self.sourceImage!, forSize: CGSize(width: resizeWidth, height: resizeHeight)) } if let resize1Width = Int(self.resize1ImageWidth), let resize1Height = Int(self.resize1ImageHeight) { self.resize1Image = ImageHelper.shared.resizeImage1(sourceImage: self.sourceImage!, forSize: CGSize(width: resize1Width, height: resize1Height)) } if let resize2Width = Int(self.resize2ImageWidth), let resize2Height = Int(self.resize2ImageHeight) { self.resize2Image = ImageHelper.shared.resizeImage1(sourceImage: self.sourceImage!, forSize: CGSize(width: resize2Width, height: resize2Height)) } } } private func choiceJoinImage() { let result: (fail: Bool, url: [URL?]?) = DialogProvider.shared.showOpenFileDialog(title: "", prompt: "", message: "選擇圖片", directoryURL: URL(fileURLWithPath: ""), allowedFileTypes: ["png", "jpg", "jpeg"], allowsMultipleSelection: true) if result.fail { return } if let urls = result.url { var imgs: [NSImage] = [] for url in urls { if let filePath = url?.path { imgs.append(NSImage(contentsOf: URL(fileURLWithPath: filePath))!) } } if imgs.count > 0 { self.joinImage = ImageHelper.shared.jointedImageWithImages(imgArray: imgs) } } } private func saveImage(image: NSImage) { let result: (isOpenFail: Bool, url: URL?) = DialogProvider.shared.showSaveDialog( title: "選擇圖片存儲路徑", directoryURL: URL(fileURLWithPath: ""), prompt: "", message: "", allowedFileTypes: ["png"] ) if result.isOpenFail || result.url == nil || result.url!.path.isEmpty { return } let exportImagePath = result.url!.path _ = ImageHelper.shared.saveImage(image: image, fileName: exportImagePath) NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: exportImagePath)]) } } struct TestImageDemo_Previews: PreviewProvider { static var previews: some View { TestImageDemo() } }
5、結(jié)尾
所有代碼已貼,并且代碼已上傳Github,見下面?zhèn)渥ⅰ?/p>
本文示例代碼:https://github.com/dotnet9/MacTest/blob/main/src/macos_test/macos_test/TestImageDemo.swift
參考文章標(biāo)題:《MAC圖像NSIMAGE縮放、組合、壓縮及CIIMAGEREF和NSIMAGE轉(zhuǎn)換處理》
參考文章鏈接:https://www.freesion.com/article/774352759/
到此這篇關(guān)于SwiftUI圖片縮放、拼圖等處理的文章就介紹到這了,更多相關(guān)SwiftUI圖片縮放、拼圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
理解二叉堆數(shù)據(jù)結(jié)構(gòu)及Swift的堆排序算法實(shí)現(xiàn)示例
二插堆即是完全二叉樹,對于排序可以按構(gòu)建最大堆或最小堆的方式來實(shí)現(xiàn),這里我們就來共同理解二叉堆數(shù)據(jù)結(jié)構(gòu)及Swift的堆排序算法實(shí)現(xiàn)示例2016-07-07swift 3.0 實(shí)現(xiàn)短信驗(yàn)證碼倒計(jì)時功能
這篇文章主要介紹了swift 3.0 實(shí)現(xiàn)短信驗(yàn)證碼倒計(jì)時功能的相關(guān)資料,需要的朋友可以參考下2017-02-02利用Swift實(shí)現(xiàn)一個響應(yīng)式編程庫
最近在學(xué)習(xí)swift,最近有空所以總結(jié)一下最近學(xué)習(xí)的內(nèi)容,下面這篇文章主要給大家介紹了關(guān)于利用Swift實(shí)現(xiàn)一個響應(yīng)式編程庫的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2017-12-12swift3.0 創(chuàng)建sqlite數(shù)據(jù)庫步驟方法
本篇文章主要介紹了swift3.0 創(chuàng)建sqlite數(shù)據(jù)庫步驟方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06用Swift構(gòu)建一個簡單的iOS郵件應(yīng)用的方法
這篇文章主要介紹了用Swift構(gòu)建一個簡單的iOS郵件應(yīng)用的方法,包括查看和標(biāo)記已讀等基本的郵件應(yīng)用功能,需要的朋友可以參考下2015-07-07switch循環(huán)所支持的數(shù)據(jù)類型案例分析
這篇文章主要介紹了switch循環(huán)所支持的數(shù)據(jù)類型,本文通過實(shí)際案例講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06淺析Swift中struct與class的區(qū)別(匯編角度底層分析)
這篇文章主要介紹了Swift中struct與class的區(qū)別 ,本文從匯編角度分析struct與class的區(qū)別,通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03