【Swift】UIGraphicsContextで空のUIImageを作る

どうも、ねこきち(@nekokichi1_yos2)です。

 

今回は、画像が入ってないUIImageを作る方法、を備忘録として書きます。

 

解説

 

UIImageは画像を格納するクラスなので、画像がなければクラスとして機能しません。

 

しかし、

「画像はないけど、指定したサイズのUIImageを用意したい」

場合がありませんか?

 

例えば、何らかの条件分岐でUIImageViewに画像を格納するかの有無を確認して、もし格納しない場合、透明の画像を入れたいなど。

 

UIImageView.image = nil、UIImageView.backgroundColor = .red、などで画像がないことを示すこともできます。

 

ですが、指定したサイズの画像をUIImageViewに入れなければ、レイアウトが崩れてしまうことがあるかもしれません。

 

そこで、画像を持たないUIImage、を作る方法を紹介します。

(変数でUIImageを生成して、frameとかbackgroundColorを設定すれば簡単なのですが、敢えて違う方法で生成する方法なので書いていきます。)

 

UIGraphicsContextを使う

 

参考記事を読んで、ざっくりまとめると

UIGraphicsContext:描画情報を管理してるオブジェクト

 

 

言わば、指定した情報(背景色、描画領域)を元に、0から画像を描画(生成)するためのもの。

 

使い方は、

  • UIGraphicsContextを生成
  • UIGraphicsContextに情報を設定
  • UIGraphicsContextで画像を生成
  • UIGraphicsContextを破棄する

 

UIGraphicsContextは、スタックと呼ばれる領域に保存され、

  • 追加
  • 取得
  • 破棄

 する際は、スタックから取り出すことになる。

 

結果

 

f:id:nekokichi_yos2:20200903081529g:plain

 

ソースコード

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var imageView: UIImageView!

    @IBAction func button(_ sender: Any) {
        let width  = imageView.frame.width
        let height = imageView.frame.height
        // 空の画像に必要nサイズ情報
        let imageCGSize = CGSize(width: width, height: height)
        // imageViewに空の画像をセット
        imageView.image = UIImage.getEmptyImage(imageColor: .red, imageSize: imageCGSize, imageView: imageView)
    }
    
}

extension UIImage {
class func getEmptyImage(color: UIColor, size: CGSize, imageView:UIImageView) -> UIImage { // グラフィックスコンテキストを作成 UIGraphicsBeginImageContext(size)
// グラフィックスコンテキスト用の位置情報 let rect = imageView.frame // グラフィックスコンテキストを取得 let context = UIGraphicsGetCurrentContext()
// グラフィックスコンテキストの設定 context!.setFillColor(color.cgColor) context!.fill(rect) // グラフィックスコンテキストの画像を取得 let image = UIGraphicsGetImageFromCurrentImageContext()! // グラフィックスコンテキストの編集を終了 UIGraphicsEndImageContext() return image } } 

 

参考

 

qiita.com

dev.classmethod.jp

ios.ch3cooh.jp

【Swift】UIImageのサイズを変更する

どうも、ねこきち(@nekokichi1_yos2)です。

 

今回は、UIImageのサイズを変更する方法を備忘録として書きます。

 

 

 

ソースコード

 

extension UIImage {
    
    func resize(size _size: CGSize) -> UIImage? {
        let widthRatio = _size.width / size.width
        let heightRatio = _size.height / size.height
        let ratio = widthRatio < heightRatio ? widthRatio : heightRatio
        
        let resizedSize = CGSize(width: size.width * ratio, height: size.height * ratio)
        
        UIGraphicsBeginImageContextWithOptions(resizedSize, false, 0.0) // 変更
        draw(in: CGRect(origin: .zero, size: resizedSize))
        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return resizedImage
    }
    
}

 

参考

 

qiita.com

develop.hateblo.jp

program-life.com

【Swift】画像を圧縮して容量を減らす

どうも、ねこきち(@nekokichi1_yos2)です。

 

Realmに画像を保存しようとした時、データ容量が大きすぎる、とのエラーに遭遇しました。

stackoverflow.com

 

そこで、画像を圧縮すると、無事にRealmへ保存できました。

 

今回は、備忘録として画像の圧縮方法、を書きます。

 

結果

 

下記のコードで、画像を圧縮する前と10%に圧縮した後のデータサイズを確認すると、 10分の1以下に圧縮されてます。

 

let pickerImageData:Int = NSData(data: pickerImage.jpegData(compressionQuality: 1)!).count
let resizedImageData:Int = NSData(data: resizedImage.jpegData(compressionQuality: 1)!).count

print(Double(pickerImageData) / 1000.0)
print(Double(resizedImageData) / 1000.0)

 

f:id:nekokichi_yos2:20200901205926p:plain

 

ソースコード

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var imageView: UIImageView!
    
    let imagePicker = UIImagePickerController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        imagePicker.delegate   = self
        imagePicker.sourceType = .photoLibrary
    }

    @IBAction func button(_ sender: Any) {
        present(imagePicker, animated: true, completion: nil)
    }
    
}

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let pickerImage = info[.originalImage] as? UIImage {
            // 10%に圧縮した画像
            let resizedImage          = pickerImage.resizeImage(withPercentage: 0.1)!
            // imageViewに挿入
            imageView.image = resizedImage
        }
        dismiss(animated: true, completion: nil)
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true, completion: nil)
    }

}

extension UIImage {
    // percentage:圧縮率
    func resizeImage(withPercentage percentage: CGFloat) -> UIImage? {
        // 圧縮後のサイズ情報
        let canvas = CGSize(width: size.width * percentage, height: size.height * percentage)
        // 圧縮画像を返す
        return UIGraphicsImageRenderer(size: canvas, format: imageRendererFormat).image {
            _ in draw(in: CGRect(origin: .zero, size: canvas))
        }
    }
}

 

参考

 

qiita.com

www.it-swarm.dev

qiita.com

 

【Swift】handlerとcompletionを変数/関数で作る

どうも、ねこきち(@nekokichi1_yos2)です。

 

Swiftには、コールバック、が存在します。

(例:handler, completion)

 

メリットは、

  • まとまった処理をブロックで持つ
  • コールバック元の処理が完了後に実行される
  • 非同期処理を順番に処理できる

の特性を持っていることです。

 

今回は、コールバックを変数や関数の形で作る方法、を備忘録として書きます。

 

解説

 

変数で定義

 

let presentHandler:() -> Void = {
    print("present")
}

 

 

let handler2 = { (value1:Int) -> Void in
    print(value1)
}

 

関数で定義

 

func alertAction(action: UIAlertAction) {
    label.isHidden = false
}
let alertController = UIAlertController(title: "アラート", message: "メッセージ", preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "アクション", style: .default, handler: alertAction(action:))) present(alertController, animated: true, completion: nil)

UIAlertController > UIAlertAction > handler、を関数で定義。

 

ただし、関数側と代入する側で型(今回の場合、action:UIAlertAction)が同じである必要がある。

 

func handler3(value2:Int) -> Void {
    print(value2)
}

 

func executeHandler(completion: (Int)->Void) {
    completion(5)
}

引数にInt型を持ち、返り値がないコールバックを引数に指定。

 

completion内に記述した数字が、引数に指定したコールバックに渡され、コールバック内の処理が行われる

 

@IBAction func handler2Button(_ sender: Any) {
    //5を出力
    executeHandler(completion: handler2)
    executeHandler(completion: handler3)
}

handler2とhandler3は、Int型の引数value1,value2をprintで出力する処理を持ち、executeHandlerで5が渡されるので、5が出力される。

 

ソースコード

 

import UIKit

class handlerViewController: UIViewController {
    
    @IBOutlet weak var label: UILabel!
    
    //アラート
    @IBAction func alertButton(_ sender: Any) {
        let alertController = UIAlertController(title: "アラート", message: "メッセージ", preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "アクション", style: .default, handler: alertAction(action:)))
        present(alertController, animated: true, completion: nil)
    }
    
    //アラートのハンドラー
    func alertAction(action: UIAlertAction) {
//labelを非表示 label.isHidden = false } //進む @IBAction func presentButton(_ sender: Any) { let sb = UIStoryboard(name: "Main", bundle: Bundle.main) let vc = sb.instantiateViewController(withIdentifier: "handler2") as! handler2ViewController //presentのハンドラー let presentHandler:() -> Void = {
//"present"を出力 print("present") } present(vc, animated: true, completion: presentHandler) } }

 

import UIKit

class handler2ViewController: UIViewController {
let handler2 = { (value1:Int) -> Void in print(value1) } func handler3(value2:Int) -> Void { print(value2) }
//引数のハンドラーを実行 func executeHandler(completion: (Int)->Void) { completion(5) } @IBAction func handler2Button(_ sender: Any) { //5を出力 executeHandler(completion: handler2) executeHandler(completion: handler3) } //dismissのハンドラー func dismissHandler() -> Void { print("dismiss") } //戻る @IBAction func dismissButton(_ sender: Any) { dismiss(animated: true, completion: dismissHandler) } }

 

参考

 

qiita.com

qiita.com