🫗
Compose MultiplatformでLiquid glass対応のタブ表示を行うには
こんにちは!sugitaniと申します。ブラックキャット・カーニバル(略称ブラキャニ)というCompose Multiplatformで作られたSNSアプリを開発しています。
本稿はブラキャニの開発で得られた"あれどうやるんだっけ" を備忘録も兼ねて共有していくシリーズの7作目です。
Liquid glass対応のタブ表示を行う
公式ドキュメントにも説明がありますが、Compose MultiplatformはUIKitと相互乗り入れが可能であり、ComposeのコンポーネントをUIViewControllerに変換することができます。
これを利用してUITabBarController
にComposeのコンポーネントを表示してもらい、iOS26で動かせばLiquid glassでのタブ移動ができます。
公式ドキュメントとほぼ同じ内容になりますが、やってみましょう
下準備
タブで表示させたい中身を作ってみましょう
composeApp/src/commonMain/kotlin/cc/bcc/cmpexamples/example007/App.kt
package cc.bcc.cmpexamples.example007
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
enum class PetType(
val emoji: String,
val bgGradient: List<Color>,
) {
CAT(
"🐱",
listOf(
Color(0xFFFFC107),
Color(0xFFFF9800),
),
),
DOG(
"🐶",
listOf(
Color(0xFF4CAF50), // 明るい緑
Color(0xFF2E7D32), // 濃い緑
),
),
}
@Composable
fun PetIconComponent(
pet: PetType,
modifier: Modifier = Modifier,
) {
Box(
modifier =
modifier.fillMaxSize().background(
brush =
Brush.verticalGradient(
colors = pet.bgGradient,
),
),
contentAlignment = Alignment.Center,
) {
Box(
modifier = Modifier.size(100.dp).background(Color.White, shape = CircleShape),
contentAlignment = Alignment.Center,
) {
Text(
text = pet.emoji,
fontSize = 64.sp,
)
}
}
}
Androidで利用
まず普通に使うとどうなるかAndroid側を実装して確認します
composeApp/src/androidMain/kotlin/cc/bcc/cmpexamples/example007/App.android.kt
package cc.bcc.cmpexamples.example007
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Pets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
class AppActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
var selectedTabIndex by remember { mutableIntStateOf(0) }
MaterialTheme {
Scaffold(
bottomBar = {
NavigationBar(containerColor = Color.Transparent) {
NavigationBarItem(
icon = { Icon(Icons.Default.Pets, contentDescription = null) },
label = { Text("Cat") },
selected = selectedTabIndex == 0,
onClick = { selectedTabIndex = 0 },
)
NavigationBarItem(
icon = { Icon(Icons.Default.Pets, contentDescription = null) },
label = { Text("Dog") },
selected = selectedTabIndex == 1,
onClick = { selectedTabIndex = 1 },
)
}
},
) {
when (selectedTabIndex) {
0 -> PetIconComponent(PetType.CAT)
1 -> PetIconComponent(PetType.DOG)
}
}
}
}
}
}
iOSで利用
iOSで使うために、まずUIViewControllerに変換するヘルパー関数を作成します
composeApp/src/iosMain/kotlin/main.kt
@file:Suppress("unused", "FunctionName")
import androidx.compose.ui.window.ComposeUIViewController
import cc.bcc.cmpexamples.example007.PetIconComponent
import cc.bcc.cmpexamples.example007.PetType
import platform.UIKit.UIViewController
fun CatViewController(): UIViewController =
ComposeUIViewController { PetIconComponent(PetType.CAT) }
fun DogViewController(): UIViewController =
ComposeUIViewController { PetIconComponent(PetType.DOG) }
次にSwift側で以下のように使います
iosApp/iosApp/iosApp.swift
import ComposeApp
import SwiftUI
@main
struct ComposeApp: App {
var body: some Scene {
WindowGroup {
ContentView().ignoresSafeArea(.all)
}
}
}
struct ContentView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let catViewController = MainKt.CatViewController()
catViewController.title = "The Cat"
let dogViewController = MainKt.DogViewController()
dogViewController.title = "The Dog"
// Set up the UITabBarController
let tabBarController = UITabBarController()
tabBarController.viewControllers = [
// Wrap them in a UINavigationController for the titles
UINavigationController(rootViewController: catViewController),
UINavigationController(rootViewController: dogViewController),
]
tabBarController.tabBar.items?[0].title = "Cat"
tabBarController.tabBar.items?[0].image = UIImage(systemName: "cat")
tabBarController.tabBar.items?[1].title = "Dog"
tabBarController.tabBar.items?[1].image = UIImage(systemName: "dog")
// Suspected iOS 26.0 beta bug: label rendering is incorrect until tab is switched, so forcing selection
tabBarController.selectedIndex = 1
tabBarController.selectedIndex = 0
return tabBarController
}
func updateUIViewController(
_ uiViewController: UIViewController,
context: Context
) {
// Updates will be handled by Compose
}
}
素直に
- タブの中身となるUIViewControllerを作成する
- UITabBarControllerを作り、VCを積載する
- UITabBarControllerを表示してもらうようにする
としているだけです
ダイアログなどもプラットフォームネイティブで表示するには?
Calfを利用すると Dialog / BottomSheet / 読み込みIndicator / DataPicker / TimePicker / WebView / File Picker / 権限リクエストなどがネイティブUIで利用出来ます。
サンプルプロジェクト
本稿のソースコード、および動作するコードは
にあります。免責事項
このコードはあくまで"自分はこう実装した"という例ですので、よりよい方法がある可能性があります。見つけたら教えてください!
以下宣伝
- コメントのしやすさともらいやすさに全力を注いだショートSNS ブラキャニ をお試しください
- ブラックキャット・カーニバル株式会社のPublicationではCompose Multiplatformに関する記事を沢山投稿しています
- Compose Multiplatformでアプリのバックグラウンド切り替えを検知するには
- Compose MultiplatformでコンポーネントをImageBitmapにするには
- Compose MultiplatformでKoinを使って環境別のDIを行う方法
- Compose Multiplatformでexpect/actialでContextをうまく扱う方法
- Compose Multiplatformで後から重ねられる&一部をくり抜いたコンポーネントを作るには
- Compose MultiplatformでImageBitmapとBitmap/UIImageを相互変換するには
- Compose MultiplatformでLiquid glass対応のタブ表示を行うには
Discussion