📱

正方形の画像からアイコン画像を生成する

2022/05/09に公開

正方形の画像からアイコン画像を生成する

要件検討

  • iOSのアイコンなので単純な角丸形状ではなく、複数のRを用いて直線部分と滑らかに接続された角丸形状にする。
  • 背景と同化する場合があるのでドロップシャドウを軽くかける。

実装方法

単純なRを用いた角丸ではないのでicon.layer.cornerRadiusは使用できない。
また、表示するサイズごとにRの値を計算するのも面倒。
よって、以下の手順で実装

  1. アイコン形状のマスクを用意
  2. マスクを使ってアイコン画像(UIImage)を切り抜き
  3. UIImageViewのimageに切り抜いたUIImageを指定し、シャドウを設定する

マスクの生成

Appleのwebで使用されていたアイコン表示のマスクイメージを拝借しベクタデータを生成。単純にPDF画像を参照しても良いのですが、UIBezierPathから生成。
サイズを指定し、一旦背景を白く塗りつぶした後にpathの形状を黒く塗りつぶす。

MaskImage

import UIKit

class MaskImage: NSObject {

	static func getMask(iconSize: CGFloat) -> UIImage {
		
		let size = CGSize(width: iconSize, height: iconSize)
		UIGraphicsBeginImageContextWithOptions(size, false, 0)
		
		let context: CGContext = UIGraphicsGetCurrentContext()!
		context.setFillColor(UIColor.white.cgColor)
		context.fill(CGRect(x: 0, y: 0, width: iconSize, height: iconSize))
		
		let path = self.maskPath()
		path.apply(CGAffineTransform(scaleX: iconSize / 256.0, y: iconSize / 256.0))
		
		UIColor.black.setFill()
		path.lineWidth = 0
		path.fill()
		
		let image = UIGraphicsGetImageFromCurrentImageContext()
		UIGraphicsEndImageContext()
		
		guard let image = image else {
			return UIImage()
		}
		return image
	}
	
	
	static func maskPath() -> UIBezierPath {
		
		let path = UIBezierPath()
		path.move(to: CGPoint(x: 256, y: 175.9))
		path.addCurve(to: CGPoint(x: 256, y: 185.1), controlPoint1: CGPoint(x: 256, y: 179), controlPoint2: CGPoint(x: 256, y: 182))
		path.addCurve(to: CGPoint(x: 255.9, y: 192.8), controlPoint1: CGPoint(x: 256, y: 187.7), controlPoint2: CGPoint(x: 255.9, y: 190.2))
		path.addCurve(to: CGPoint(x: 254.4, y: 209.6), controlPoint1: CGPoint(x: 255.8, y: 198.4), controlPoint2: CGPoint(x: 255.3, y: 204.1))
		path.addCurve(to: CGPoint(x: 249.1, y: 225.6), controlPoint1: CGPoint(x: 253.4, y: 215.2), controlPoint2: CGPoint(x: 251.7, y: 220.6))
		path.addCurve(to: CGPoint(x: 225.6, y: 249.1), controlPoint1: CGPoint(x: 244, y: 235.7), controlPoint2: CGPoint(x: 235.7, y: 244))
		path.addCurve(to: CGPoint(x: 209.6, y: 254.4), controlPoint1: CGPoint(x: 220.6, y: 251.7), controlPoint2: CGPoint(x: 215.2, y: 253.4))
		path.addCurve(to: CGPoint(x: 192.8, y: 255.8), controlPoint1: CGPoint(x: 204.1, y: 255.3), controlPoint2: CGPoint(x: 198.5, y: 255.8))
		path.addCurve(to: CGPoint(x: 185.1, y: 256), controlPoint1: CGPoint(x: 190.3, y: 255.9), controlPoint2: CGPoint(x: 187.7, y: 256))
		path.addCurve(to: CGPoint(x: 175.9, y: 256), controlPoint1: CGPoint(x: 182, y: 256), controlPoint2: CGPoint(x: 179, y: 256))
		path.addLine(to: CGPoint(x: 80.1, y: 256))
		path.addCurve(to: CGPoint(x: 70.9, y: 256), controlPoint1: CGPoint(x: 77, y: 256), controlPoint2: CGPoint(x: 74, y: 256))
		path.addCurve(to: CGPoint(x: 63.2, y: 255.9), controlPoint1: CGPoint(x: 68.3, y: 256), controlPoint2: CGPoint(x: 65.8, y: 255.9))
		path.addCurve(to: CGPoint(x: 46.4, y: 254.4), controlPoint1: CGPoint(x: 57.6, y: 255.8), controlPoint2: CGPoint(x: 51.9, y: 255.3))
		path.addCurve(to: CGPoint(x: 30.4, y: 249.1), controlPoint1: CGPoint(x: 40.8, y: 253.4), controlPoint2: CGPoint(x: 35.4, y: 251.6))
		path.addCurve(to: CGPoint(x: 6.9, y: 225.6), controlPoint1: CGPoint(x: 20.3, y: 244), controlPoint2: CGPoint(x: 12, y: 235.7))
		path.addCurve(to: CGPoint(x: 1.6, y: 209.6), controlPoint1: CGPoint(x: 4.3, y: 220.6), controlPoint2: CGPoint(x: 2.6, y: 215.2))
		path.addCurve(to: CGPoint(x: 0.1, y: 192.8), controlPoint1: CGPoint(x: 0.7, y: 204), controlPoint2: CGPoint(x: 0.2, y: 198.4))
		path.addCurve(to: CGPoint(x: 0, y: 185.1), controlPoint1: CGPoint(x: 0.1, y: 190.2), controlPoint2: CGPoint(x: 0, y: 187.6))
		path.addCurve(to: CGPoint(x: 0, y: 175.9), controlPoint1: CGPoint(x: 0, y: 182), controlPoint2: CGPoint(x: 0, y: 179))
		path.addLine(to: CGPoint(x: 0, y: 80.1))
		path.addCurve(to: CGPoint(x: 0, y: 70.9), controlPoint1: CGPoint(x: 0, y: 77), controlPoint2: CGPoint(x: 0, y: 74))
		path.addCurve(to: CGPoint(x: 0.1, y: 63.2), controlPoint1: CGPoint(x: 0, y: 68.3), controlPoint2: CGPoint(x: 0.1, y: 65.8))
		path.addCurve(to: CGPoint(x: 1.6, y: 46.4), controlPoint1: CGPoint(x: 0.2, y: 57.6), controlPoint2: CGPoint(x: 0.7, y: 51.9))
		path.addCurve(to: CGPoint(x: 6.9, y: 30.4), controlPoint1: CGPoint(x: 2.6, y: 40.8), controlPoint2: CGPoint(x: 4.3, y: 35.4))
		path.addCurve(to: CGPoint(x: 30.4, y: 6.9), controlPoint1: CGPoint(x: 12, y: 20.3), controlPoint2: CGPoint(x: 20.3, y: 12))
		path.addCurve(to: CGPoint(x: 46.4, y: 1.6), controlPoint1: CGPoint(x: 35.4, y: 4.3), controlPoint2: CGPoint(x: 40.8, y: 2.6))
		path.addCurve(to: CGPoint(x: 63.2, y: 0.1), controlPoint1: CGPoint(x: 51.9, y: 0.7), controlPoint2: CGPoint(x: 57.5, y: 0.2))
		path.addCurve(to: CGPoint(x: 70.9, y: 0), controlPoint1: CGPoint(x: 65.8, y: 0.1), controlPoint2: CGPoint(x: 68.3, y: 0))
		path.addCurve(to: CGPoint(x: 80.1, y: 0), controlPoint1: CGPoint(x: 74, y: 0), controlPoint2: CGPoint(x: 77, y: 0))
		path.addLine(to: CGPoint(x: 175.9, y: 0))
		path.addCurve(to: CGPoint(x: 185.1, y: 0), controlPoint1: CGPoint(x: 179, y: 0), controlPoint2: CGPoint(x: 182, y: 0))
		path.addCurve(to: CGPoint(x: 192.8, y: 0.1), controlPoint1: CGPoint(x: 187.7, y: 0), controlPoint2: CGPoint(x: 190.2, y: 0.1))
		path.addCurve(to: CGPoint(x: 209.6, y: 1.6), controlPoint1: CGPoint(x: 198.4, y: 0.2), controlPoint2: CGPoint(x: 204.1, y: 0.7))
		path.addCurve(to: CGPoint(x: 225.6, y: 6.9), controlPoint1: CGPoint(x: 215.2, y: 2.6), controlPoint2: CGPoint(x: 220.6, y: 4.3))
		path.addCurve(to: CGPoint(x: 249.1, y: 30.4), controlPoint1: CGPoint(x: 235.7, y: 12), controlPoint2: CGPoint(x: 244, y: 20.3))
		path.addCurve(to: CGPoint(x: 254.4, y: 46.4), controlPoint1: CGPoint(x: 251.7, y: 35.4), controlPoint2: CGPoint(x: 253.4, y: 40.8))
		path.addCurve(to: CGPoint(x: 255.8, y: 63.2), controlPoint1: CGPoint(x: 255.3, y: 51.9), controlPoint2: CGPoint(x: 255.8, y: 57.5))
		path.addCurve(to: CGPoint(x: 256, y: 70.9), controlPoint1: CGPoint(x: 255.9, y: 65.8), controlPoint2: CGPoint(x: 255.9, y: 68.3))
		path.addCurve(to: CGPoint(x: 256, y: 80.1), controlPoint1: CGPoint(x: 256, y: 74), controlPoint2: CGPoint(x: 256, y: 77))
		path.addLine(to: CGPoint(x: 256, y: 175.9))
		path.close()
		
		return path
	}
}

取得した画像データからマスクを生成し、UIImageに適用

UIImage+Extension

import UIKit

extension UIImage {
	
	var masking : UIImage? {
		
		let maskImage:UIImage = MaskImage.getMask(iconSize: self.size.width)
		guard let maskImage = maskImage.cgImage else {
			return nil
		}
		
		//マスクを作成する
		let mask = CGImage(maskWidth: maskImage.width,
						   height: maskImage.height,
						   bitsPerComponent: maskImage.bitsPerComponent,
						   bitsPerPixel: maskImage.bitsPerPixel,
						   bytesPerRow: maskImage.bytesPerRow,
						   provider: maskImage.dataProvider!,
						   decode: nil, shouldInterpolate: false)!
		
		//マスクを適用する
		guard let maskedImage = self.cgImage?.masking(mask) else {
			return nil
		}
		
		let resultImage = UIImage(cgImage: maskedImage)
		
		return resultImage
	}
}

UIImageVIewへの適用とドロップシャドウ

UIIMageView+Extension

import UIKit

extension UIImageView {
	
	/// アイコンエフェクトを適用
	var appIconEffect: UIImageView {
		
		if let iconimage = self.image?.masking {
			self.image = iconimage
		}
		
		// ドロップシャドウを適用
		return self.dropShadow
	}
	
	/// ドロップシャドウを定義 
	var dropShadow: UIImageView {
		
		self.layer.shadowColor = UIColor.black.cgColor
		self.layer.shadowRadius = 10.0
		self.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
		self.layer.shadowOpacity = 0.3
		
		return self
	}
}

アイコンのフェクトの適用

その1

UIImageView
@IBOutlet weak var icon: UIImageView!
	
// ViewDidLoadでもViewWillAppearでも適当な場所でどうそ
icon = icon.appIconEffect

その2

UIImageView
let icon = UIImageView(image: UIImage(named: "sampleIcon"))
icon.frame = CGRect(x: 60, y: 280, width: 120, height: 120)
self.view.addSubview(icon.appIconEffect)

もし、シャドウが枠内にしか表示されない場合は...
icon.layer.masksToBounds = false
これでOK!

Discussion