まいまいワークス

主にiPhoneアプリの開発で考えた事、調べた事、感じた事などを記していきます。

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

要件検討

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

実装方法

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

  1. アイコン形状のマスクを用意
  2. マスクを使ってアイコン画像(UIImage)を切り抜き
  3. 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!