【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は、スタックと呼ばれる領域に保存され、
- 追加
- 取得
- 破棄
する際は、スタックから取り出すことになる。
結果
ソースコード
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 } }
参考
【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 } }
参考
【Swift】画像を圧縮して容量を減らす
どうも、ねこきち(@nekokichi1_yos2)です。
Realmに画像を保存しようとした時、データ容量が大きすぎる、とのエラーに遭遇しました。
そこで、画像を圧縮すると、無事に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)
ソースコード
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)) } } }
参考
【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) } }
参考