swift指令碼程式設計:一鍵生成AppIcon

W_C__L發表於2019-02-28

自從Xcode8之後就不支援外掛了,沒法用Xcode一鍵生成AppIcon,一直沒找到好的解決方案,一怒之下決定自己寫一個指令碼用來生成AppIcon,下面是正文,小弟拋磚引玉,有寫的不好的地方有請大佬們見諒:

原始碼地址

事前準備

檢視swift版本

首先你要確定你的Mac上的swift版本:

swift --version複製程式碼

我電腦上的執行結果是這樣的:

Apple Swift version 4.0 (swiftlang-900.0.65 clang-900.0.37)
Target: x86_64-apple-macosx10.9複製程式碼

然後就可以用Xcode建一個swift檔案來編寫swift指令碼了,不過單獨建一個swift檔案,Xcode編輯起來非常不友好,我的方案是建一個在Mac上執行的Command Line Tool工程,這樣的話有程式碼提示,要不然寫起來太痛苦,如果大佬們有更好的辦法,可以指導一下小弟。

swift指令碼程式設計小知識

終端輸入和輸出

剛入手指令碼我們第一件事前就應該瞭解在終端如何進行輸入和輸出,下面是輸入和輸出的辦法:

輸出

輸入很簡單,大家也很熟悉,就是print,下面是程式碼示例:

print("Hello world!")複製程式碼

然後大家可以執行以下試試(test.swift是你的檔名):

swift test.swift複製程式碼

執行後就能在終端上看到一行字:Hello world!

這樣子我們的第一個swift指令碼就完成了。

輸入

知道了怎麼輸出我們還得知道怎麼輸入,輸入也非常簡單,下面是程式碼示例:

print("請輸入文字:")
if let input = readLine() {
    print("你輸入的文字:(input)")
}複製程式碼

執行之後顯示的結果:

請輸入文字:
Hello world!
你輸入的文字:Hello world!複製程式碼

這樣輸入也完成了,我們也算swift指令碼程式設計入門了。

在swift指令碼中呼叫其他命令

我們經常用的命令有很多,比如echo、mkdir、cd等等,我們能不能在swift中直接呼叫呢,答案是可以的,下面我們用簡單的例子來了解一下,大家想深入的話可以去研究一下傳送門

import Foundation

func execute(path: String, arguments: [String]? = nil) -> Int {
    let process = Process()
    process.launchPath = path
    if arguments != nil {
        process.arguments = arguments!
    }
    process.launch()
    process.waitUntilExit()
    return Int(process.terminationStatus)

}

let status = execute(path: "/bin/ls")
print("Status = (status)")複製程式碼

以上的指令碼相當於在終端中執行了ls命令,如果大家不知道命令的路徑的話,可以用where查詢一下,例如:

where ls複製程式碼

這是執行後的結果:

ls: aliased to ls -G
/bin/ls複製程式碼

這裡的/bin/ls就是ls命令的路徑。

開始編寫指令碼

讀取input.png

首先我們要從將需要轉化的圖片讀取出來,下面是主要程式碼:

import Foundation

let inputPath = "input.png"
let inoutData = try Data(contentsOf: url)
print("圖片大小:(inoutData.count / 1024) kb")
let dataProvider = CGDataProvider(data: inoutData as CFData)
if let inputImage = CGImage(pngDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent) {
    /// inputImage就是需要轉化的圖片
}else {
    print("轉換失敗,圖片必須是png格式")
}複製程式碼

生成AppIcon.appiconset和Contents.json

這裡就設計到檔案操作了,用FileManager就行了,相信大家已經輕車熟路了,我就貼一些主要程式碼,大家看完整版去我的github原始碼看就行了:

import Foundation

/// AppIcon的model
struct AppIconImageItem: Codable {
    let size: String
    let idiom: String
    let filename: String
    let scale: String
    let role: String?
    let subtype: String?
}

struct AppIconInfo: Codable {
    let version: Int
    let author: String
}

struct AppIcon: Codable {
    var images: [AppIconImageItem]
    let info: AppIconInfo
}


/// 建立contentsJson
///
/// - Parameter appIcon: 傳入的appIcon
func createAppIconContentsJson(appIcon: AppIcon) {
    print("
開始生成contentsJson
")
    let encoder = JSONEncoder()
    do {
        encoder.outputFormatting = .prettyPrinted
        let appIconData = try encoder.encode(appIcon)
        if let appIconStr  = String.init(data: appIconData, encoding: .utf8) {
            let contentJsonPath = "AppIcon.appiconset/Contents.json"
            let contentJsonUrl = URL(fileURLWithPath: contentJsonPath)
            try appIconStr.write(to: contentJsonUrl, atomically: true, encoding: .utf8)
            print("contentsJson生成成功
")
        }else {
            print("contentsJson生成失敗")
        }
    }catch {
        print(error.localizedDescription)
    }
}

/// 建立appicon檔案
///
/// - Parameter appIcon: appicon
func createFile(appIcon: AppIcon, image: CGImage) {
    let fileManager = FileManager.default
    let filePath = "AppIcon.appiconset"
    do {
        if fileManager.fileExists(atPath: filePath) {
            try fileManager.removeItem(atPath: filePath)
        }
        try fileManager.createDirectory(atPath: filePath, withIntermediateDirectories: true, attributes: nil)
        createAppIconContentsJson(appIcon: appIcon)
        print("~~~~~~~~~~~~~~完成~~~~~~~~~~~~~~")
    }catch {
        print("檔案目錄(filePath)建立失敗")
        print(error.localizedDescription)
    }
}複製程式碼

生成不同尺寸的image

生成圖片我們用的是Foundation框架裡面的Core Graphics框架,下面是主要程式碼:

import Foundation


/// 生成單個image
///
/// - Parameters:
///   - size: 圖片的size
///   - scale: 倍數,例如@2x就是2倍
///   - filename: 檔名
func createImage(size: CGSize, scale: CGFloat, image: CGImage, filename: String) {
    print("開始生成圖片: (filename)")
    let width  = Int(size.width * scale)
    let height = Int(size.height * scale)
    let bitsPerComponent = image.bitsPerComponent
    let bytesPerRow = image.bytesPerRow
    let colorSpace  = image.colorSpace

    if let context = CGContext.init(data: nil,
                                    width: width,
                                    height: height,
                                    bitsPerComponent: bitsPerComponent,
                                    bytesPerRow: bytesPerRow,
                                    space: colorSpace!,
                                    bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) {
        context.interpolationQuality = .high
        context.draw(image, in: .init(origin: .zero, size: .init(width: width, height: height)))
        if let inputImage = context.makeImage() {
            let outputImagePath = "AppIcon.appiconset/(filename)"
            let outputUrl = URL(fileURLWithPath: outputImagePath) as CFURL
            let destination = CGImageDestinationCreateWithURL(outputUrl, kUTTypePNG, 1, nil)
            if let destination = destination {
                CGImageDestinationAddImage(destination, inputImage, nil)
                if CGImageDestinationFinalize(destination) {
                    print("圖片: (filename) 生成成功
")
                }else {
                    print("圖片: (filename) 生成失敗
")
                }
            }
        }else {
            print("圖片: (filename) 生成失敗
")
        }
    }
}複製程式碼

最後給大家貼以下完成的截圖:

上面只是一部分主要程式碼,完整的程式碼太多了,大家可以去我的github地址上去下載執行以下試試,如果有什麼做的不好的地方,歡迎大家指教~~

相關文章