Help us understand the problem. What is going on with this article?

Neumorphism: 令和時代のスキューモーフィズム

※この記事はデザインのトレンドを紹介する体で書かれた技術記事&ライブラリの宣伝となっています。あらかじめご了承ください。

tl;dr;

みなさんはNeumorphismという単語をご存知でしょうか?

これは次に流行るとちらほら言われているデザインのトレンドで例えばこんなものです。
image.png
Neumorphism in user interfacesより

少しスキューモーフィズムのようなリアル感があるのがわかるでしょうか?
これは命名もnew + skeuomorphismから来ているそうで、そこまでリアリティを追求しているわけではないんだけど、フラットやマテリアルよりもっと質感を感じられるようなデザインとなっています。

これはかっこいい。ですが、実装はどうやるのか?気になりますよね。

そこで今回はこのNeumorphismをiOSネイティブで実装するにはどうすればよいのかについて紹介していきます。

そもそもどうやったらこんな感じになるのん?

実装するためにまずはNeumorphism自体についてもっと理解しなければなりません。
それに関してはこのNeumorphism (Soft UI) in User interface design -Tutorialという記事が参考になります。

この記事自体はSketchやFigma、illustratorでどのようにNeumorphismを実現するのかが解説されているのですが、重要な部分は2点。(以下は上記記事の画像です。)

ポイント1:2つの影を使う

image.png

まず1つ目に重要なのは影を2つ使うということです。片方がベース色より明るい、もう一方がベース色より暗い色の影を使います。

ポイント2:影に使う色はベース色の輝度のみ変えたものを使う

image.png

厳密には輝度以外が変わっていてもブレンドされていれば大丈夫だとは思いますが、このように並べるとグラデーションになるように色をチョイスすればよいです。

以上を行うとどんな色でもneumorphismと呼ばれるような見た目になってくれます。

iOSでどうやって実装する?

ではこれをiOSでどのように実装すればいいでしょうか?

大前提としてUIViewは影を一個しかつけられないのでそこを解決しなくてはなりません。

方法としては

  1. CALayerをサブレイヤーとして追加していく
  2. 影用のViewをつくる

が考えられます。

try1: CALayerの罠

影だけであれば1つ目の方法で実装可能です。大体こんな感じ。

うまくいってるようですが、このときに角丸をつけようとするとうまくいきません。いわゆるmaskedToBoundsのジレンマにはまります。(なぜか一応できているStackOverflowもあるんですが...)

そこで2のほうの方法を取ることにしました(Viewが増えまくるので苦渋の決断笑)

try2: 色をどうするか?

さてShadowの付け方は決まりました。次に重要なのは影の色になります。
毎回デザインソフトなどで色をブレンドして...というのは面倒ですよね。できればベース色を決めたら勝手ににゅってなってほしい。。。

そこでこれはHSLの値を直接いじることにしました。

といっても、SwiftはHSBしか対応していません。そこで変換を用意してあげます。(参考

private func hsbToHsl(h: CGFloat, s: CGFloat, b: CGFloat) -> (h: CGFloat, s: CGFloat, l: CGFloat) {

    let newH = h
    var newL = (2 - s) * b
    var newS = s * b
    newS /= newL <= 1 ? newL : 2 - newL
    newL /= 2
    return (h: newH, s: newS, l: newL)
}

private func hslToHsb(h: CGFloat, s: CGFloat, l: CGFloat) -> (h: CGFloat, s: CGFloat, b: CGFloat) {
    let newH = h
    let ll = l * 2
    let ss = s * (ll <= 1 ? ll : 2 - ll)
    let newB = (ll + ss) / 2
    let newS = (2 * ss) / (ll + ss)
    return (h: newH, s: newS, b: newB)
}

これによってHSLでの計算が可能になったので、それをもとにこんなメソッドをUIColorにつけてあげました。

func lighterColor(value: CGFloat) -> UIColor {
    var h: CGFloat = 0
    var s: CGFloat = 0
    var b: CGFloat = 0
    var a: CGFloat = 0

    guard getHue(&h, saturation: &s, brightness: &b, alpha: &a) else { return self }

    let hsl = hsbToHsl(h: h, s: s, b: b)
    let hsb = hslToHsb(h: hsl.h, s: hsl.s, l: hsl.l + value)

    return UIColor(hue: hsb.h, saturation: hsb.s, brightness: hsb.b, alpha: a)
}

このメソッドを使えばvalueに渡した量だけもとの色より輝度が明るい色を作ることができます。

これで下準備は完了です。実際にこのようなextensionをつかってNeumorphismが適用できるようにしました。

public extension UIView {

    func addNeumorphismShadow(with parent: UIView, dist: CGFloat = 9, blur: CGFloat = 16) {

        guard let backgroundColor = backgroundColor else { return }
        let shadowViews = neumorphismShadowViews(color: backgroundColor, dist: dist, blur: blur)
        shadowViews.forEach {
            parent.insertSubview($0, belowSubview: self)
        }
    }

    func neumorphismShadowViews(color: UIColor, dist: CGFloat, blur: CGFloat) -> [UIView] {

        let lightShadowView: UIView = {
            let lightView = UIView(frame: frame)
            let lightColor = color.lighterColor(value: 0.12)
            lightView.backgroundColor = .white
            lightView.layer.applyShadow(color: lightColor, alpha: 0.5, x: -dist, y: -dist, blur: blur)
            lightView.layer.cornerRadius = layer.cornerRadius
            lightView.layer.maskedCorners = layer.maskedCorners
            return lightView
        }()
        let darkShadowView: UIView = {
            let darkView = UIView(frame: frame)
            let darkColor = color.darkerColor(value: 0.18)
            darkView.backgroundColor = .white
            darkView.layer.applyShadow(color: darkColor, alpha: 0.5, x: dist, y: dist, blur: blur)
            darkView.layer.cornerRadius = layer.cornerRadius
            darkView.layer.maskedCorners = layer.maskedCorners
            return darkView
        }()
        return [lightShadowView, darkShadowView]
    }
}

デフォルト値はチュートリアルの推奨値を使っています。

応用する

さて、ここまででNeumorphismをどうやって実装するかをざっくりと見てきました(正直力技感がありますが笑)

じゃあこれの使い所はどこか?となったときに最近Pinterestなどが取り入れているフローティングタブバーのNeumorphism版があればいいのでは...?となりました。

なので、実装しちゃいました。デデン!

neumorphismtab.png

NeumorphismTab!!!!!

こちらから試すことができます。MITライセンスです。

どんなものかというと普通のTabBarControllerと同じような感覚でNeumorphismなTabBarを使えてしまうという代物です。具体的にはこのように使います。

import UIKit
import NeumorphismTab

class DemoTabBarController: NeumorphismTabBarController {

    override func setupView() {

        let color = UIColor(hex: "C1D2EB")
        let home = NeumorphismTabBarItem(icon: UIImage(systemName: "house.fill")!, title: "Home")
        let favorite = NeumorphismTabBarItem(icon: UIImage(systemName: "heart.fill")!, title: "")

        view.backgroundColor = color

        let homeViewController: ViewController = {
            let viewController = ViewController.instantiate()
            viewController.view.backgroundColor = color
            return viewController
        }()

        let favoriteViewController: UIViewController = {
            let viewController = UIViewController()
            viewController.view.backgroundColor = color
            return viewController
        }()

        setTabBar(items: [home, favorite])
        viewControllers = [homeViewController, favoriteViewController]
    }
}

そうするとこのようにそれっぽいタブが使えるようになります。

image.png

すべてのViewControllerが同じ背景色である必要があるなど、デザイン的にどうしてもという部分はありますが、かなり簡単に使えると言えるものになったと思います。

これで出すアプリは未定ですが、もしよろしければぜひ使ってみてください!適宜アップデートします。あともしこうしたほうがいいのでは?というのがあればPRも待っています。

Cocoapods/Carthage/SwiftPackageManagerに対応しています。

まとめ

というわけで今回はNeumorphismの簡単な紹介と、それに伴い作成したライブラリを紹介させていただきました。

それほどモバイルにこのUIが取り入れられていくのかというところはわかりませんが(Viewの数が尋常じゃないし)今後もこんな感じでなういUIを実装していけたらと思います。

では最後にもう一度こちら
neumorphismtab.png
touyou/NeumorphismTab

今回記事に載せなかった実装なども見れるのでぜひ参考にしてみてください。あとスターもお願いします!笑

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away