要件検討
- iOSのアイコンなので単純な角丸形状ではなく、複数のRを用いて直線部分と滑らかに接続された角丸形状にする。
- 背景と同化する場合があるのでドロップシャドウを軽くかける。
実装方法
単純なRを用いた角丸ではないのでicon.layer.cornerRadius
は使用できない。
また、表示するサイズごとにRの値を計算するのも面倒。
よって、以下の手順で実装
- アイコン形状のマスクを用意
- マスクを使ってアイコン画像(UIImage)を切り抜き
- 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!