はじめに
SwiftUIが徐々に浸透していっている中、まだまだUIKitがメインなのが現状だと思います。
ただStoryboardもコンフリクトが起きやすいなどのデメリットが多いのも事実・・
UIKitでStoryboardを使わず、かつ宣言的で簡単にレイアウトを組むことができれば1番ですよね。
それを実現できる便利なライブラリを今回は紹介したいと思います。
補足
ライブラリとは関係ないですが,ホットリロード機能が使えるようになるツールもご紹介します。
ホットリロードとは簡単にいうと,その都度ビルドしてUIを確認することなくソースコードの変更を即座にシュミレーターで確認できる便利機能です(flutterだと標準装備ですが・・)
まずは下記からツールをダウンロードしておきましょう↓
https://github.com/johnno1962/InjectionIII/releases/tag/4.3.2
ダウンロードしたらApplicationフォルダに入っていることを確認し、一度開きましょう。
上部バーに注射器アイコンが表示されていればOKです。
ライブラリ導入
次にSPMかPodsでライブラリを入れていきます。
dependencies: [
.package(url: "https://github.com/sakiyamaK/DeclarativeUIKit", .upToNextMajor(from: "0.2"))
]
pod 'DeclarativeUIKit'
プロジェクトを作成したら、AppDelegateに下記を記述していきます。
ツールを入れた場所をpathで指定してます
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
#if DEBUG
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle")?.load()
#endif
return true
}
つぎは から初期画面を立ち上げるための処理です
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
self.window = window
let vc = DeclarativeViewController()
window.rootViewController = vc
window.makeKeyAndVisible()
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
}
}
これで初期画面が立ち上がります。
DeclarativeViewControllerのソースコードを見ていきましょう。
import UIKit
import DeclarativeUIKit
extension Notification.Name {
static let injection = Notification.Name("INJECTION_BUNDLE_NOTIFICATION")
}
extension NotificationCenter {
func addInjectionObserver(_ observer: Any, selector: Selector, object: Any?) {
NotificationCenter.default.addObserver(observer, selector: selector, name: .injection, object: object)
}
}
final class DeclarativeViewController: UIViewController {
override func loadView() {
super.loadView()
NotificationCenter.default.addInjectionObserver(self, selector: #selector(setupLayout), object: nil)
setupLayout()
}
}
// ここでレイアウトを組んでいく
@objc private extension DeclarativeViewController {
func setupLayout() {
//初期時に画面を白にしておく
self.view.backgroundColor = .white
self.declarative {
// ここに記述していく
}
}
}
loadViewdでレイアウト変更通知を受け取れるようにセットしておいて、レイアウトが変更されたら通知が送られるという感じです。
self.view.backgroundColor = .white
と記述しないと画面が真っ黒なままになってしまうので注意です。
あとはself.declarative {}
としてその中で宣言的に記述していけばいいだけ。
ホットリロードの準備
- 上部バーの注射器アイコンをタップし、
Open Project
(1番上)を選択 - 作成したプロジェクトファイル(podで入れた場合は~.xcworkspaceのファイル)を選択
これで対象のプロジェクトのみにホットリロードを適用することができます!
一度ビルドすると、下記のようにデバッグコンソールに表示されたらOK
実際のホットリロード画面はこちら↓
感動...
ソースコード
さて、本題であるDeclarativeUIKitの使い方を見ていきましょう。
self.declarative {
UIStackView.vertical {
UIStackView.horizontal {}
.backgroundColor(.green)
UIStackView.horizontal {}
.backgroundColor(.red)
UIStackView.horizontal {}
.backgroundColor(.brown)
}
.distribution(.fillEqually)
}
上のサンプルみたく、UIStackViewの中にさらにUIStackViewが3つ入ってる状態です。
下記のようにプロパティを並べていけるので直感的に分かりやすいです
self.declarative {
UIStackView.vertical {
UIStackView.horizontal {
UIView()
.size(width: 100, height: 100)
.backgroundColor(.red)
.cornerRadius(30)
.border(color: .blue, width: 10)
}
}
}
また、宣言的に書くなかでも
「今までみたいに命令的に書きたい!」
って時に便利なのがimperative
で
self.declarative {
UIStackView.vertical {
UILabel()
.imperative { label in
let label = label as! UILabel
label.text = "hogehoge"
}
.backgroundColor(.gray)
.height(100)
UIView()
.imperative { view in
view.tintColor = .black
view.isUserInteractionEnabled = true
}
UIView.spacer()
.backgroundColor(.brown)
}
}
こんな感じで今まで通りに書くことができます。
レイアウトは宣言的に書いて、その他の処理はimperativeの中にぶち込むなんてこともできちゃいますね
おわりに
他にも色々使い方が満載なので、やりながら覚えていきたいと思います!
ホットリロード+DeclarativeUIKit
でレイアウト作りは爆速になりそうな予感ですね👀
一応サンプルのリポジトリを貼っておきます↓
ぜひ試してみてください