正方形の画像からアイコン画像を生成する
要件検討
- iOSのアイコンなので単純な角丸形状ではなく、複数のRを用いて直線部分と滑らかに接続された角丸形状にする。
- 背景と同化する場合があるのでドロップシャドウを軽くかける。
実装方法
単純なRを用いた角丸ではないのでicon.layer.cornerRadius
は使用できない。
また、表示するサイズごとにRの値を計算するのも面倒。
よって、以下の手順で実装
- アイコン形状のマスクを用意
- マスクを使ってアイコン画像(UIImage)を切り抜き
- UIImageViewのimageに切り抜いたUIImageを指定し、シャドウを設定する
マスクの生成
Appleのwebで使用されていたアイコン表示のマスクイメージを拝借しベクタデータを生成。単純にPDF画像を参照しても良いのですが、UIBezierPathから生成。
サイズを指定し、一旦背景を白く塗りつぶした後にpathの形状を黒く塗りつぶす。
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に適用
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への適用とドロップシャドウ
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
@IBOutlet weak var icon: UIImageView! // ViewDidLoadでもViewWillAppearでも適当な場所でどうそ icon = icon.appIconEffect
その2
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!