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

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

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

そういえばRealm...2025/10からはMongoのサポートはなくなってOSSになる(あくまで自分の認識)、というところでどうなるのだろう🤔
SwiftDataへの移行とかは現実的なのか??
現実的だとしても、個人アプリにおいてローカルDBを別のものへとマイグレーションするのとかは怖いな(やるなら、しばらくは両方のDBで運用するとかが良いのかな)

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

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

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

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

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

Build a UIKit app with the new design を観た。
docよりも動いている様子なんかも見れるので、WWDCの動画はやっぱ良い。
以後、このセッションを見ながら、iOS26対応の方法などを手を動かしながら見ていく。

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

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

こちらのナビゲーションバーのボタン群のグルーピングの挙動をいくつか確かめてみた(今回対応するアプリにはこんなにたくさんボタンを置くことない)。
テキストボタン
画像ボタン
.done
にしてみる
画像ボタンのうち1つを確かに、WWDCのセッション通りの挙動になる。

細かなものだけど、UINavigationItem
に、iOS26からsubtitle
なんてのが生えている。
@available(iOS 26.0, *)
open var subtitle: String?
色合いを調整していないから見づらいけどこんな感じ。
LargeTitleのときにしか出ないものなのかなみたいに思っていたけどそんなことはなかった(LargeTitleでももちろん表示される)

- LargeTitleに変更
- UINavigationBarまわりのappearanceの設定を一旦全て削除
- UITableViewのtopの制約をSafeAreaからSuperviewへ変更
でLiquid Glass感ぽくなった。

↑ Liquid Glassに「がっつり対応」すると、アプリのメインカラー色が一気に弱まって、全部似たようなアプリになってしまうなぁという所感。
いくつかのApple純正アプリ(ポットキャスト、メモ、App Store)も下に並べてみたけど、そんな感じがする。
いつかこの透明感に対するカウンターパンチとしてのデザインも出てきそう。
とはいえ、ホーム画面のアプリアイコンの「クリア」や「色合い調整」といった機能や、あといまはどうなったのか知らないけどAndroidのMaterial Youなんていう話もあったから、「アプリのメインカラー」という概念ももう古いのかな?

UIScrollEdgeElementContainerInteraction
を使って以下のgifのところでエッジエフェクトを適用してみようと思ったのだけど、うまく動かず...
if #available(iOS 26.0, *) {
let interaction = UIScrollEdgeElementContainerInteraction()
interaction.scrollView = tableView
interaction.edge = .top
headerViewContainer.addInteraction(interaction)
}
tableViewのclipToBoundsをfalseにしてみたり、なんならz軸上に重ねたりもしてみたけどダメ。
一旦後回し。

ナビゲーションの遷移におけるfluidityの話、面白いなぁ。
メモアプリでのデモでしていたこと、
- 遷移中にスワイプバックでキャンセルできる
- 遷移中にコンテンツのスクロールができる
- 戻るボタン連打で高速に戻る
実際に触ってみると、実使いにおいては前者2つは片手ではなかなかありがたみは感じられないかもという印象はあるが...とてもfluidityだ!!

というか7年前のpotatotipsでそんな発表をしていたのだった。
まさに「遷移途中のキャンセル」「遷移中にスクロール」というアイデアは自分でも考えていた!

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

以下業務で困ったことだけど、Menuのトランジッションが変わったことによる悪い影響が出てしまった。

sheet表示をzoom transitionできるようになったということでやってみた、動いた。
if #available(iOS 18.0, *) {
vc.preferredTransition = .zoom(sourceViewProvider: { [weak self]_ in
self?.addButton
})
}
というか、iOS18からあったのかということでiOS18のsimulatorで動かしてみたところ、同じような挙動になった。

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

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)
// }

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

FABをLiquid Glass対応してみた。
裏に画像とかがあると綺麗なのだけど、ほとんどのタイミングで裏はベタ塗りの白背景なので、あまり意味がないかも...
var config = UIButton.Configuration.glass()
config.image = UIImage(systemName: "plus")
addButton = .init(configuration: config)

他にも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はグラスなのか?🤔

UIScrollEdgeElementContainerInteraction
について、再度トライ。
interactionをaddする方のビューの下に、tableViewを潜り込ませる(重ねる)形にしたらようやく適用されたけど、、、そんなことしなくちゃいけないなんてことないよね??

エッジエフェクトが適用される高さについてもよくわからない。潜り込ませる量を増やしたら、スクショのようになってしまった(わかりづらいけど、一定量の高さまでエッジエフェクトが適用されて、それ以降は何もされない)。
ちなみにUIScrollEdgeElementContainerInteraction
をaddしたあとには、以下のビューが追加されていることは確認できた。

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

空のプロジェクトでの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)
}