画像の上に別の画像を重ねたり説明文をいれたりしたい場合には、UIGraphicsBeginImageContextを使うと画像とテキストを合成できます。画像などを重ねるには他にも方法はありますが選択肢の一つです。
この例のように1枚の画像にテキストとアイコン画像を重ねて合成してみます。
画像とテキストの合成
例として以下の写真、アイコン画像とテキストを合成して見ます。
これらとテキスト
Emmy’s shopping in London.
画像は、Retina対応(@2xなど)としてAssets.xcassets に入れます。
Retina 画像でない場合は
UIGraphicsBeginImageContextWithOptions
を使わないと画像が粗くなる場合があります。
UIGraphicsBeginImageContext
これを実行するために、UIGraphicsBeginImageContext を呼び出し、
終了したら UIGraphicsEndImageContext() で終わります。
その2つの間で作業します。Graphics context 上に UIImageの draw(in:) メソッドを使って画像、テキストなど一つづつ順番にレンダリングしていき、最終的に出来上がったGraphics contextには合成されたImageができているという仕組みです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// Emmy画像の UIImage インスタンスを生成 let imageEmmy:UIImage! = UIImage(named:"emmy") ... UIGraphicsBeginImageContext(imageEmmy.size) // EmmyをUIImageのdrawInRectメソッドでレンダリング imageEmmy.draw(in: rect) // テキストの描画領域 let textRect = CGRect(x:100, y:imageHeight/2+0, width:imageWidth-250, height:80) let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle let textFontAttributes = [ NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: UIColor.yellow, NSAttributedString.Key.paragraphStyle: textStyle ] // テキストをdrawInRectメソッドでレンダリング text.draw(in: textRect, withAttributes: textFontAttributes) // ハンドバッグの描画領域 let handbagRect = CGRect(x:imageWidth/2+100, y:imageHeight/2-20, width:imageHandbagWidth*3/2, height:imageHandbagHeight*3/2) // ハンドバッグをdrawInRectメソッドでレンダリング imageHandbag.draw(in: handbagRect) // Context に描画された画像を新しく設定 let newImage = UIGraphicsGetImageFromCurrentImageContext(); // Context 終了 UIGraphicsEndImageContext() |
サンプルコード
横長で表示させるケースです。
ViewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 画面背景色を設定してみました self.view.backgroundColor = UIColor(red:0.99,green:0.81,blue:0.56,alpha:1.0) } // viewWillLayoutSubviews以降でないとsafeAreaInsetsは取得できない override func viewDidAppear(_ animated: Bool) { // 画面の横幅を取得 // 以降、Landscape のみを想定 let screenWidth:CGFloat = view.frame.size.width let screenHeight:CGFloat = view.frame.size.height setup(sW: screenWidth, sH: screenHeight) } func setup(sW: CGFloat, sH: CGFloat){ // 改行して2行で表示 let text = "Emmy's shopping in \nLondon." let font = UIFont.boldSystemFont(ofSize: 20) // Emmy画像の UIImage インスタンスを生成 let imageEmmy:UIImage! = UIImage(named:"emmy") // Emmy画像の幅・高さの取得 let imageWidth = imageEmmy.size.width let imageHeight = imageEmmy.size.height // 描画領域を生成 let rect = CGRect(x:0, y:0, width:imageWidth, height:imageHeight) // ハンドバッグの UIImage インスタンスを生成 let imageHandbag:UIImage! = UIImage(named:"handbag") // ハンドバッグの幅・高さの取得 let imageHandbagWidth = imageHandbag.size.width let imageHandbagHeight = imageHandbag.size.height // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Context 開始 // 一番大きい画像サイズでContextを開く UIGraphicsBeginImageContext(imageEmmy.size) // Retinaで画像が粗い場合 //UIGraphicsBeginImageContextWithOptions(imageEmmy.size, false, 0) // EmmyをUIImageのdrawInRectメソッドでレンダリング imageEmmy.draw(in: rect) // テキストの描画領域 let textRect = CGRect(x:100, y:imageHeight/2+0, width:imageWidth-250, height:80) let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle let textFontAttributes = [ NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: UIColor.yellow, NSAttributedString.Key.paragraphStyle: textStyle ] // テキストをdrawInRectメソッドでレンダリング text.draw(in: textRect, withAttributes: textFontAttributes) // ハンドバッグの描画領域 let handbagRect = CGRect(x:imageWidth/2+100, y:imageHeight/2-20, width:imageHandbagWidth*3/2, height:imageHandbagHeight*3/2) // ハンドバッグをdrawInRectメソッドでレンダリング imageHandbag.draw(in: handbagRect) // Context に描画された画像を新しく設定 let newImage = UIGraphicsGetImageFromCurrentImageContext(); // Context 終了 UIGraphicsEndImageContext() // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // UIImageView インスタンス生成 let newImageView = UIImageView() newImageView.image = newImage let newImageWidth = newImage!.size.width let newImageHeight = newImage!.size.height // 画像サイズをスクリーン幅に合わせる, scaling let scale:CGFloat = sW / newImageWidth let newRect = CGRect(x: 0, y: 0, width: newImageWidth * scale, height: newImageHeight * scale) // frame をCGRectで作った矩形に合わせる newImageView.frame = newRect // 画像の中心をスクリーンの中央あたりにする let adjust:CGFloat = 40 newImageView.center = CGPoint(x: sW/2 , y: sH/2 + adjust) // view に ImageView を追加する self.view.addSubview(newImageView) } } |
新しくできた UIImage を UIImageView でスケールしてスクリーンに描画しています。
そのため、Emmy画像が大きい分、余計にメモリを食っている可能性があり、UIGraphicsBeginImageContext(CGSize: size)
のsizeを最終画像サイズにすればいいのかもしれません。あるいは別の方法もあるようですが
上下(ここでは左右)のマージン、safeAreaInsetsを考慮してエリア内に収まるようにしてみたのですが。
ただ、この場合ボタンや他のUIが被らないのであればむしろ画面を大きく使う事が望ましいと思われますので
こっちの方がいいでしょうね
関連ページ
- UIImageView の設定(コードで記述
- ストーリーボードを使った画像 UIImageView の設定
- 画像の拡大縮小 (CGRectMake)
- CGAffineTransform:画像を回転、移動、反転
- アニメーション(パラパラマンガ)
- Image, Text の合成
- 画像をドラッグさせる
- UIImage の使い方
References:
UIGraphicsBeginImageContext – UIKit | Apple Developer Documentation
UIGraphicsEndImageContext() – UIKit | Apple Developer Documentation
draw(in:) – UIImage | Apple Developer Documentation