Open31

個人アプリ「Kopi」をiOS26対応(主にLiquid Glass対応)していくうえでの雑ログ

ushisantoasobuushisantoasobu

まずはXcode26でビルドできるようにする。
業務で同僚がやってみていた感じだと、ビルド通るまではそんな大変じゃないはず。

ushisantoasobuushisantoasobu

Xcode26でビルドしようとしたら、Realmまわりでエラーが出た。
だいぶ古いの使っていた + いまだにCocoaPods経由だったので、最新にする + SPMへ移行。
...これでビルド通った🎉

ushisantoasobuushisantoasobu

そういえばRealm...2025/10からはMongoのサポートはなくなってOSSになる(あくまで自分の認識)、というところでどうなるのだろう🤔

https://x.com/ushisantoasobu/status/1833661306827931984

SwiftDataへの移行とかは現実的なのか??
現実的だとしても、個人アプリにおいてローカルDBを別のものへとマイグレーションするのとかは怖いな(やるなら、しばらくは両方のDBで運用するとかが良いのかな)

ushisantoasobuushisantoasobu

まずはXcode26でビルドしてみて、どんな感じになったか(Liquid Glass)のチェック。個人アプリなので良くも悪くも標準UIで済ませているので、大枠は変な感じはない。
というか、1箇所クラッシュするようになっている、、、ScrollViewが無限ループするようになってしまった。何か内部の挙動が変わったのかな。
そもそもここはScrollViewを強引な手で「無限ループ」をしている箇所なので、まあそれを止めるというのもありかもしれない。

ushisantoasobuushisantoasobu

広告(AdMob)はどうレイアウトするのが良いだろう?
よくあるやつだと思うけど、これまでのUITabBarの上に乗せるような形で置いていたもの。
あとFABもLiquid Glassに対応した方が良いのだろうか?🤔

ushisantoasobuushisantoasobu

SFSafariViewControllerを用いて利用ガイドページを表示しているが(全然更新していない💦)、タブとSFSafariViewControllerのUIが被っているな。

ushisantoasobuushisantoasobu

NavBarのボタンが長いテキストだとダサく感じてしまう。かと言って、テキストの方がわかりやすいケースもあると思う。他のアプリも見てみよう。

ushisantoasobuushisantoasobu

alertについては認知していたけど、actionSheetもこんな風になったのね。

ushisantoasobuushisantoasobu
tabBarController.tabBarMinimizeBehavior = .onScrollDown

ただ現状のレイアウトだとタブバーに何もコンテンツ表示されていないので意味ない...

ushisantoasobuushisantoasobu

SideBarの背景が透過するやつ(UIBackgroundExtensionView)は、裏では左右反転したものが表示されているということなのか。

ushisantoasobuushisantoasobu

こちらのナビゲーションバーのボタン群のグルーピングの挙動をいくつか確かめてみた(今回対応するアプリにはこんなにたくさんボタンを置くことない)。

テキストボタン

画像ボタン

画像ボタンのうち1つを.doneにしてみる

確かに、WWDCのセッション通りの挙動になる。

ushisantoasobuushisantoasobu

細かなものだけど、UINavigationItemに、iOS26からsubtitleなんてのが生えている。

@available(iOS 26.0, *)
open var subtitle: String?

色合いを調整していないから見づらいけどこんな感じ。
LargeTitleのときにしか出ないものなのかなみたいに思っていたけどそんなことはなかった(LargeTitleでももちろん表示される)

ushisantoasobuushisantoasobu
  • LargeTitleに変更
  • UINavigationBarまわりのappearanceの設定を一旦全て削除
  • UITableViewのtopの制約をSafeAreaからSuperviewへ変更

でLiquid Glass感ぽくなった。

ushisantoasobuushisantoasobu

↑ Liquid Glassに「がっつり対応」すると、アプリのメインカラー色が一気に弱まって、全部似たようなアプリになってしまうなぁという所感。
いくつかのApple純正アプリ(ポットキャスト、メモ、App Store)も下に並べてみたけど、そんな感じがする。
いつかこの透明感に対するカウンターパンチとしてのデザインも出てきそう。



とはいえ、ホーム画面のアプリアイコンの「クリア」や「色合い調整」といった機能や、あといまはどうなったのか知らないけどAndroidのMaterial Youなんていう話もあったから、「アプリのメインカラー」という概念ももう古いのかな?

ushisantoasobuushisantoasobu

UIScrollEdgeElementContainerInteractionを使って以下のgifのところでエッジエフェクトを適用してみようと思ったのだけど、うまく動かず...

if #available(iOS 26.0, *) {
    let interaction = UIScrollEdgeElementContainerInteraction()
    interaction.scrollView = tableView
    interaction.edge = .top
    headerViewContainer.addInteraction(interaction)
}

tableViewのclipToBoundsをfalseにしてみたり、なんならz軸上に重ねたりもしてみたけどダメ。
一旦後回し。

ushisantoasobuushisantoasobu

ナビゲーションの遷移におけるfluidityの話、面白いなぁ。

メモアプリでのデモでしていたこと、

  • 遷移中にスワイプバックでキャンセルできる
  • 遷移中にコンテンツのスクロールができる
  • 戻るボタン連打で高速に戻る

実際に触ってみると、実使いにおいては前者2つは片手ではなかなかありがたみは感じられないかもという印象はあるが...とてもfluidityだ!!

ushisantoasobuushisantoasobu

エッジだけでなくどこからでも戻るスワイプで1つ前の画面に戻れるようになったの最高すぎる。

ushisantoasobuushisantoasobu

sheet表示をzoom transitionできるようになったということでやってみた、動いた。

if #available(iOS 18.0, *) {
    vc.preferredTransition = .zoom(sourceViewProvider: { [weak self]_  in
        self?.addButton
    })
}

というか、iOS18からあったのかということでiOS18のsimulatorで動かしてみたところ、同じような挙動になった。

ushisantoasobuushisantoasobu

sheetの見た目がglassになったという話(小さいときはglassぽく、大きくなったときはそうではなくなる)。
sheet内のビューに塗りがなければそうなる。

ushisantoasobuushisantoasobu

ActionSheetをinlineで、つまりこれまでのiPadみたく表示できるようになったと。
コードはこれまでのiPad通りで良さそう(下のコードは、iPadの条件分岐を外しただけのもの)。

inlineのときはキャンセルボタンは自動で非表示になる(どこか別の箇所タップで閉じるから)。
また以下のコードのままiOS18で動かしたときにどうなるのだっけというのがわからなかったので試したところ、これまでのActionSheetが表示されるだけだった(バージョンによる条件分岐は必要なさそう)。

//        if UIDevice.current.userInterfaceIdiom == .pad {
            let sourceView: UIView = tableView.cellForRow(at: indexPath) ?? self.view
            action.popoverPresentationController?.sourceView = sourceView
            action.popoverPresentationController?.sourceRect = .init(x: sourceView.frame.size.width / 2,
                                                                     y: sourceView.frame.size.height / 2,
                                                                     width: 1,
                                                                     height: 1)
//        }

ushisantoasobuushisantoasobu

Searchは現状アプリで考える必要なさそうなので一旦無視。
GitHubアプリであーだこーだ言われているからちゃんと追ってはいく。

ushisantoasobuushisantoasobu

FABをLiquid Glass対応してみた。
裏に画像とかがあると綺麗なのだけど、ほとんどのタイミングで裏はベタ塗りの白背景なので、あまり意味がないかも...

var config = UIButton.Configuration.glass()
config.image = UIImage(systemName: "plus")
addButton = .init(configuration: config)

ushisantoasobuushisantoasobu

他にもGlass系のConfigurationがいくつかある。

// Creates a configuration for a button that has a Liquid Glass style.
static func glass() -> UIButton.Configuration

// Creates a configuration for a button that has a prominent Liquid Glass style.
static func prominentGlass() -> UIButton.Configuration

// Creates a configuration for a button that has a clear Liquid Glass style.
static func clearGlass() -> UIButton.Configuration

// Creates a configuration for a button that has a prominent, clear Liquid Glass style.
static func prominentClearGlass() -> UIButton.Configuration

最小限の実装で動かしてみた("+"ボタンが4つ並んでいるところ)。
左から、glass, prominentGlass, clearGlass, prominentClearGlass。
prominentGlassはグラスなのか?🤔

ushisantoasobuushisantoasobu

エッジエフェクトが適用される高さについてもよくわからない。潜り込ませる量を増やしたら、スクショのようになってしまった(わかりづらいけど、一定量の高さまでエッジエフェクトが適用されて、それ以降は何もされない)。

ちなみにUIScrollEdgeElementContainerInteractionをaddしたあとには、以下のビューが追加されていることは確認できた。

ushisantoasobuushisantoasobu

UIScrollEdgeElementContainerInteractionがよくわからなくて悔しいので、空のプロジェクトを作ってもう少し調べてみる。
が、、、あれ?interactionが効かないぞ?なんだこれ、としばしハマってしまったが、addInteractionするビューの中身が空(subviewsがない)だと適用されないのね、、、

https://x.com/SebJVidal/status/1955641030893851102 で知った 🙇

ushisantoasobuushisantoasobu

空のプロジェクトでのUIScrollEdgeElementContainerInteractionの調査続き。

bottomのは効く。

でも同じように実装してもtopが効かない。

ここからは同僚のtoshi0383さんから教えてもらったもの(こちらの記事を見て調べてくれた)を自分なりに解釈したものですが、まずnavigationBarを外すと、topも正しく動く。

そこから察するに、navigationBar周りが何か悪さ(今回やりたいことに対して)をしているのではというところで深掘ってみると、、、

(lldb) po navigationController?.navigationBar.interactions
▿ Optional<Array<UIInteraction>>
  ▿ some : 2 elements
    - 0 : <_UIPointerInteractionAssistant: 0x600002938620>
    - 1 : <_UIScrollPocketContainerInteraction: 0x600000024280>

navigationBarのinteractionsに、こちらでも見かけたUIScrollPocketたるものがある。
おそらくこれがエッジエフェクトなのかな?
いずれにせよこれを強引に取り除くと、navigationBarあり且つtopが効くようになったことは確認できた。

if let i = navigationController?.navigationBar.interactions.last {
    navigationController?.navigationBar.removeInteraction(i)
}