Let me try out the reorderable container API in iOS 27
This page has been translated by machine translation. View original
Introduction
Let's actually try out the new APIs reorderable() / reorderContainer() added in iOS 27
Now that an official API for implementing reordering has been released, reordering has become much easier to implement
Based on the session, it seems this can be applied not just to List but to various containers, which should increase UI flexibility
It also appears to work on watchOS and other platforms
Testing Environment
- Xcode 27 beta 6
- iOS 27.0 Simulator
※ This is verification using a beta version. Specifications may change before the official release.
First, let's read the documentation and write some code
import SwiftUI
import OrderedCollections
@main struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
// Sample data
struct Landmark: Identifiable {
let id = UUID()
let name: String
}
struct ContentView: View {
@State private var landmarks: [Landmark] = [
Landmark(name: "Mt. Fuji"),
Landmark(name: "Kiyomizudera"),
Landmark(name: "Himeji Castle"),
Landmark(name: "Itsukushima Shrine"),
Landmark(name: "Tokyo Tower"),
]
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() //New API
}
.reorderContainer(for: Landmark.self) { difference in
// Update the array
difference.apply(to: &landmarks)
} //New API
.padding()
}
}
// Array reordering
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
}
}
Implementation based on the above references
The WWDC video mentioned using swift-collections, so I tried creating a helper function that seemed appropriate, but honestly I'm not sure what the correct implementation looks like
Behavior

Looks great
Reordering works fine even with VStack!
Trying different containers
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
// Update the array
difference.apply(to: &landmarks)
}
.padding()
}

Hard to tell from the GIF, but there's occasional stuttering when moving items. It works, but the behavior is a bit unstable
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()
}

This one works nicely
Issues noticed while trying it out
When first implementing with VStack as shown in the official documentation, a warning appears during reordering
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.

I looked into it a bit but couldn't find any information about the cause of this warning related to reorderContainer
This warning appears in the VStack / LazyVGrid implementations above, but not in List
The warning disappears when wrapped in a ScrollView
I found that wrapping VStack / LazyVGrid in a ScrollView eliminates this warning. My guess is that List didn't show the warning because it's a scrollable View, but the details are unclear
Workaround below:
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()
}
}
Summary
Pros
- Reordering works with various containers
- Being able to leave animation implementation to the system without implementing it yourself is incredibly convenient
Cons
- Depending on the container, there can be a feeling of stutter when grabbing or reordering items
- You'll understand if you try it on an actual device
Things I don't fully understand
- Is my helper function implementation for reordering actually correct?
- There were some slightly unstable behaviors, but since it's Beta, I'm hoping for improvements going forward
- I still don't know what the best practices are, and there isn't much information available
Finally
There are other things I wanted to test, and I wasn't able to dig too deep into it, but if I feel like it, I'll explore further in a separate article!