iOS 27のreorderable container APIで並べ替えを試してみる

iOS 27のreorderable container APIで並べ替えを試してみる

iOS 27で新しく追加された`reorderable()`と`reorderContainer()`というAPIを実際に使ってみました。VStack、List、LazyVGridなど複数のコンテナで並び替え機能を実装してみた結果をまとめています。
2026.09.06

はじめに

https://developer.apple.com/swiftui/whats-new/

iOS 27で追加された新APIの reorderable() / reorderContainer() の実際に使ってみる

並べ替えを実装する際の公式APIが実装されたのでより簡単に並べ替えができるようになった

セッションを見る限り、Listだけでなくいろんなコンテナに適用できるというものらしいのでUIの自由度が上がりそう

watch OSなどでも動作するみたい

検証環境

  • Xcode 27 beta 6
  • iOS 27.0 Simulator

※ベータ版での検証です。正式版までに仕様が変わる可能性があります。

ひとまずドキュメントを見てコードを書いてみる

import SwiftUI
import OrderedCollections

@main struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

// 仮のデータ
struct Landmark: Identifiable {
    let id = UUID()
    let name: String
}

struct ContentView: View {
    @State private var landmarks: [Landmark] = [
        Landmark(name: "富士山"),
        Landmark(name: "清水寺"),
        Landmark(name: "姫路城"),
        Landmark(name: "厳島神社"),
        Landmark(name: "東京タワー"),
    ]

    var body: some View {
        VStack {
            ForEach(landmarks) { landmark in
                Text(landmark.name)
                    .padding()
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8))
            }
            .reorderable() //新API
        }
        .reorderContainer(for: Landmark.self) { difference in
            // 配列の更新を行う
            difference.apply(to: &landmarks)
        } //新API
        .padding()
    }
}

// 配列の並び替え
extension ReorderDifference where CollectionID == ReorderableSingleCollectionIdentifier {
    func apply(to values: inout [some Identifiable<ItemID>]) {
        var dictionary = OrderedDictionary(uniqueKeys: values.map { $0.id }, values: values)
        let destinationOffset: Int? = switch destination.position {
        case .before(let destination):
            dictionary.keys.firstIndex(of: destination)
        case .end:
            nil
        }
        dictionary.move(keys: sources, to: destinationOffset ?? values.endIndex)
        values = dictionary.values.elements
    }
}

https://developer.apple.com/videos/play/wwdc2026/269/?time=918

https://developer.apple.com/documentation/SwiftUI/View/reorderContainer(for:isEnabled:move:)

https://developer.apple.com/documentation/swiftui/reordering-items-in-lists-stacks-grids-and-custom-layouts

この辺りを参考に実装

WWDCの動画ではswift-collectionsを使っていると言っていたのでひとまずそれっぽいヘルパー関数を作ってみているが、正直どう実装するのが正解か分からない

動き

Screen Recording iPhone 17 Pro 2026-09-06 at 20.16.18

いい感じ

VStackでも問題なく並び替えができている!

別のコンテナに変えてみる

List

    var body: some View {
        List {
            ForEach(landmarks) { landmark in
                Text(landmark.name)
                    .padding()
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8))
            }
            .reorderable()
        }
        .reorderContainer(for: Landmark.self) { difference in
            // 配列の更新を行う
            difference.apply(to: &landmarks)
        }
        .padding()
    }

Screen Recording iPhone 17 Pro 2026-09-06 at 20.38.52

GIFでは分からないがアイテムを移動させたりするとちょっとカクつく時がある。動作はしてるが挙動がちょっと怪しい

LazyVGrid

    private let columns = [GridItem(.adaptive(minimum: 100), spacing: 12)]

    var body: some View {
        LazyVGrid(columns: columns, spacing: 12) {
            ForEach(landmarks) { landmark in
                Text(landmark.name)
                    .padding()
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8))
            }
            .reorderable()
        }
        .reorderContainer(for: Landmark.self) { difference in
            difference.apply(to: &landmarks)
        }
        .padding()
    }

Screen Recording iPhone 17 Pro 2026-09-06 at 21.04.58

こちらは良い感じに動作

触ってて気づいた問題

最初に公式ドキュメントにあるようにVStackで実装してみたが並び替え動作時に警告が出る

Adding 'UIView' as a subview of UIHostingController.view is not supported and may result in a broken view hierarchy. Add your view above UIHostingController.view in a common superview or insert it into your SwiftUI content in a UIViewRepresentable instead.

スクリーンショット 2026-09-06 21.01.43

少し調べてみたがreorderContainerと関連付くこの警告の原因については情報が見当たらなかった

上記のVStack / LazyVGridの実装ではこの警告が出るがListでは出ない

ScrollViewで囲ったら警告が出なくなった

VStack / LazyVGridをScrollViewで囲うとこの警告はなくなることがわかった、前述のListでは警告が出なかったのはスクロール可能なViewだから出なかったのかなと推測が立ったが詳細は不明

以下回避策

    var body: some View {
        ScrollView {
            VStack {
                ForEach(landmarks) { landmark in
                    Text(landmark.name)
                        .padding()
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8))
                }
                .reorderable()
            }
            .reorderContainer(for: Landmark.self) { difference in
                difference.apply(to: &landmarks)
            }
            .padding()
        }
    }

まとめ

良いところ

  • いろんなコンテナで並び替えができる
  • アニメーションの実装なくシステム任せにできるのはめっちゃ楽

悪いところ

  • コンテナによってはアイテムを掴んだり並び替えたりする時に引っ掛かりがある感じがある?
    • 実際に手元で動かしてみてもらえれば分かると思う

良く分かんないこと

  • 並び替えのヘルパー関数の実装だけど、これで良いのか???
  • ちょっと挙動が怪しいところはあったがBetaなので今後改善されることを期待
  • ベストプラクティス的なのがまだわかっていないし、情報が少ない

最後に

他にも検証してみたいことがあったり、あんまり深掘りできなかったけど気が向いたら別の記事で検証してみます!

この記事をシェアする

関連記事