※この記事はデザインのトレンドを紹介する体で書かれた技術記事&ライブラリの宣伝となっています。あらかじめご了承ください。
tl;dr;
みなさんはNeumorphismという単語をご存知でしょうか?
これは次に流行るとちらほら言われているデザインのトレンドで例えばこんなものです。
Neumorphism in user interfacesより
少しスキューモーフィズムのようなリアル感があるのがわかるでしょうか?
これは命名もnew + skeuomorphismから来ているそうで、そこまでリアリティを追求しているわけではないんだけど、フラットやマテリアルよりもっと質感を感じられるようなデザインとなっています。
これはかっこいい。ですが、実装はどうやるのか?気になりますよね。
そこで今回はこのNeumorphismをiOSネイティブで実装するにはどうすればよいのかについて紹介していきます。
そもそもどうやったらこんな感じになるのん?
実装するためにまずはNeumorphism自体についてもっと理解しなければなりません。
それに関してはこのNeumorphism (Soft UI) in User interface design -Tutorialという記事が参考になります。
この記事自体はSketchやFigma、illustratorでどのようにNeumorphismを実現するのかが解説されているのですが、重要な部分は2点。(以下は上記記事の画像です。)
ポイント1:2つの影を使う
まず1つ目に重要なのは影を2つ使うということです。片方がベース色より明るい、もう一方がベース色より暗い色の影を使います。
ポイント2:影に使う色はベース色の輝度のみ変えたものを使う
厳密には輝度以外が変わっていてもブレンドされていれば大丈夫だとは思いますが、このように並べるとグラデーションになるように色をチョイスすればよいです。
以上を行うとどんな色でもneumorphismと呼ばれるような見た目になってくれます。
iOSでどうやって実装する?
ではこれをiOSでどのように実装すればいいでしょうか?
大前提としてUIViewは影を一個しかつけられないのでそこを解決しなくてはなりません。
方法としては
- CALayerをサブレイヤーとして追加していく
- 影用の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!!!!!
こちらから試すことができます。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]
}
}
そうするとこのようにそれっぽいタブが使えるようになります。
すべてのViewControllerが同じ背景色である必要があるなど、デザイン的にどうしてもという部分はありますが、かなり簡単に使えると言えるものになったと思います。
これで出すアプリは未定ですが、もしよろしければぜひ使ってみてください!適宜アップデートします。あともしこうしたほうがいいのでは?というのがあればPRも待っています。
Cocoapods/Carthage/SwiftPackageManagerに対応しています。
まとめ
というわけで今回はNeumorphismの簡単な紹介と、それに伴い作成したライブラリを紹介させていただきました。
それほどモバイルにこのUIが取り入れられていくのかというところはわかりませんが(Viewの数が尋常じゃないし)今後もこんな感じでなういUIを実装していけたらと思います。
では最後にもう一度こちら
touyou/NeumorphismTab
今回記事に載せなかった実装なども見れるのでぜひ参考にしてみてください。あとスターもお願いします!笑