View in English

  • Apple Developer
    • 今すぐ始める

    「今すぐ始める」を詳しく見る

    • 概要
    • 学ぶ
    • Apple Developer Program

    最新情報

    • 最新ニュース
    • Hello Developer
    • プラットフォーム

    プラットフォームを詳しく見る

    • Appleプラットフォーム
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    特集

    • デザイン
    • 配信
    • ゲーム
    • アクセサリ
    • Web
    • Home
    • CarPlay
    • テクノロジー

    テクノロジーを詳しく見る

    • 概要
    • Xcode
    • Swift
    • SwiftUI

    特集

    • アクセシビリティ
    • App Intent
    • Apple Intelligence
    • ゲーム
    • 機械学習とAI
    • セキュリティ
    • Xcode Cloud
    • コミュニティ

    コミュニティを詳しく見る

    • 概要
    • 「Appleに相談」イベント
    • コミュニティによるイベント
    • デベロッパフォーラム
    • オープンソース

    特集

    • WWDC
    • Swift Student Challenge
    • デベロッパストーリー
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Center
    • ドキュメント

    ドキュメントを詳しく見る

    • ドキュメントライブラリ
    • テクノロジー概要
    • サンプルコード
    • ヒューマンインターフェイスガイドライン
    • ビデオ

    リリースノート

    • 注目のアップデート
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • ダウンロード

    ダウンロードを詳しく見る

    • すべてのダウンロード
    • オペレーティングシステム
    • アプリ
    • デザインリソース

    特集

    • Xcode
    • TestFlight
    • フォント
    • SF Symbols
    • Icon Composer
    • サポート

    サポートを詳しく見る

    • 概要
    • ヘルプガイド
    • デベロッパフォーラム
    • フィードバックアシスタント
    • お問い合わせ

    特集

    • アカウントヘルプ
    • App Reviewガイドライン
    • App Store Connectヘルプ
    • 近日導入予定の要件
    • 契約およびガイドライン
    • システムステータス
  • クイックリンク

    • イベント
    • ニュース
    • Forum
    • サンプルコード
    • ビデオ
 

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • すべてのビデオ
  • 利用方法

その他のビデオ

  • 概要
  • Summary
  • コード
  • Code Along:SwiftUIによるパワフルなドラッグ&ドロップ機能の構築

    一緒にソリティアゲームを作りながら、SwiftUIの最新のドラッグ&ドロップ機能について学びましょう。ユーザーがコンテンツを整理できる機能を並べ替えのための新しいAPIにより実装する方法、複数のアイテムを一度に移動できるドラッグコンテナを実装する方法、アプリのルールに合わせてドラッグ&ドロップのライフサイクルをカスタマイズする方法を紹介します。このセッションの内容を十分理解できるよう、WWDC22の「Meet Transferable」を視聴することをおすすめします。

    関連する章

    • 0:00 - Introduction
    • 1:42 - Reordering
    • 6:50 - Drag multiple items
    • 9:59 - Drag configuration
    • 14:29 - Next steps

    リソース

    • Making a card game with drag, drop, and reordering in SwiftUI
    • Drag and drop
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC22

    • Transferableの紹介
  • このビデオを検索
    • 3:40 - Add reorderable to the preview

      #Preview {
          @Previewable @State var cards = [
              CardValue(rank: .ace, suit: .clubs),
              CardValue(rank: .ace, suit: .diamonds),
              CardValue(rank: .ace, suit: .hearts),
              CardValue(rank: .ace, suit: .spades)
          ]
      
          HStack {
              ForEach(cards) { card in
                  CardFaceView(card: card)
              }
              .reorderable()
          }
          .frame(maxWidth: .infinity, maxHeight: .infinity)
          .reorderContainer(for: CardValue.self) { difference in
              cards.apply(difference: difference)
          }
          .padding()
          .background(.green.gradient)
      }
    • 4:40 - Add reorder container to the GameView

      struct GameView: View {
          var game: Game
      
          var body: some View {
              GeometryReader { proxy in
                  let spacing: CGFloat = 10
                  let cardWidth = (proxy.size.width - 6 * spacing) / 7
                  VStack {
                      HStack(alignment: .top, spacing: spacing) {
                          Group {
                              RemainderView(game: game)
                              CardBackView()
                                  .hidden()
                              ForEach(CardValue.Suit.allCases) { suit in
                                  DestinationView(game: game, suit: suit)
                              }
                          }
                          .frame(width: cardWidth)
                      }
                      .padding(.bottom, 20)
                      HStack(alignment: .top, spacing: spacing) {
                          ForEach(0..<7) { index in
                              PileView(game: game, index: index)
                                  .frame(width: cardWidth)
                          }
                      }
                      .frame(maxHeight: .infinity, alignment: .top)
                    	// Add the reorder container modifier.
                      .reorderContainer(for: CardValue.self, in: Card.Group.self) { difference in
                          game.moveCards(difference: difference)
                      }
                  }
              }
              .padding()
          }
      }
    • 5:58 - Add reorderable to PileView

      struct PileView: View {
          var game: Game
          var index: Int
          @Query var cards: [Card]
      
          var body: some View {
              ZStack(alignment: .topLeading) {
                  CardPlaceholderView()
                  PileLayout {
                      let index = firstFaceUpIndex
                    	// Iterates over the face down cards.
                      ForEach(cards[..<index]) { card in
                          CardView(card: card)
                      }
                      // Iterates over the face up cards.
                      ForEach(cards[index...], id: \.value) { card in
                          CardView(card: card)
                      }
                      .reorderable(collectionID: Card.Group.pile(index))
                  }
              }
          }
      
          var firstFaceUpIndex: Int {
              cards.firstIndex { !$0.isFaceDown } ?? cards.endIndex
          }
      }
    • 7:50 - Add dragContainer to customize the reorderContainer modifier.

      struct GameView: View {
          var game: Game
      
          var body: some View {
              GeometryReader { proxy in
                  let spacing: CGFloat = 10
                  let cardWidth = (proxy.size.width - 6 * spacing) / 7
                  VStack {
                      HStack(alignment: .top, spacing: spacing) {
                          Group {
                              RemainderView(game: game)
                              CardBackView()
                                  .hidden()
                              ForEach(CardValue.Suit.allCases) { suit in
                                  DestinationView(game: game, suit: suit)
                              }
                          }
                          .frame(width: cardWidth)
                      }
                      .padding(.bottom, 20)
                      HStack(alignment: .top, spacing: spacing) {
                          ForEach(0..<7) { index in
                              PileView(game: game, index: index)
                                  .frame(width: cardWidth)
                          }
                      }
                      .frame(maxHeight: .infinity, alignment: .top)
                      .reorderContainer(for: CardValue.self, in: Card.Group.self) { difference in
                          game.moveCards(difference: difference)
                      }
                      // Add dragContainer to customize reorderContainer.
                      .dragContainer(for: CardValue.self) { cardID in
                          game.cardStack(startingAt: cardID)
                      }
                  }
              }
              .padding()
          }
      }
    • 8:45 - Add dragPreviewsFormation to customize how the dragged cards appear

      struct GameView: View {
          var game: Game
      
          var body: some View {
              GeometryReader { proxy in
                  let spacing: CGFloat = 10
                  let cardWidth = (proxy.size.width - 6 * spacing) / 7
                  VStack {
                      HStack(alignment: .top, spacing: spacing) {
                          Group {
                              RemainderView(game: game)
                              CardBackView()
                                  .hidden()
                              ForEach(CardValue.Suit.allCases) { suit in
                                  DestinationView(game: game, suit: suit)
                              }
                          }
                          .frame(width: cardWidth)
                      }
                      .padding(.bottom, 20)
                      HStack(alignment: .top, spacing: spacing) {
                          ForEach(0..<7) { index in
                              PileView(game: game, index: index)
                                  .frame(width: cardWidth)
                          }
                      }
                      .frame(maxHeight: .infinity, alignment: .top)
                      .reorderContainer(for: CardValue.self, in: Card.Group.self) { difference in
                          game.moveCards(difference: difference)
                      }
                      .dragContainer(for: CardValue.self) { cardID in
                          game.cardStack(startingAt: cardID)
                      }
                    	// Have dragged cards appear as a stack.
                      .dragPreviewsFormation(.stack)
                  }
              }
              .padding()
          }
      }
    • 9:14 - Add dropPreviewsFormation to customize how dragged cards appear over a destination

      struct GameView: View {
          var game: Game
      
          var body: some View {
              GeometryReader { proxy in
                  let spacing: CGFloat = 10
                  let cardWidth = (proxy.size.width - 6 * spacing) / 7
                  VStack {
                      HStack(alignment: .top, spacing: spacing) {
                          Group {
                              RemainderView(game: game)
                              CardBackView()
                                  .hidden()
                              ForEach(CardValue.Suit.allCases) { suit in
                                  DestinationView(game: game, suit: suit)
                              }
                          }
                          .frame(width: cardWidth)
                      }
                      .padding(.bottom, 20)
                      HStack(alignment: .top, spacing: spacing) {
                          ForEach(0..<7) { index in
                              PileView(game: game, index: index)
                                  .frame(width: cardWidth)
                          }
                      }
                      .frame(maxHeight: .infinity, alignment: .top)
                      .reorderContainer(for: CardValue.self, in: Card.Group.self) { difference in
                          game.moveCards(difference: difference)
                      }
                      .dragContainer(for: CardValue.self) { cardID in
                          game.cardStack(startingAt: cardID)
                      }
                      .dragPreviewsFormation(.stack)
                  }
                  // Have a consistent appearance over drop destinations.
                  .dropPreviewsFormation(.stack)
              }
              .padding()
          }
      }
    • 11:40 - Add a drag configuration to allow move.

      struct RemainderView: View {
          @Query var cards: [Card]
          var game: Game
      
          var body: some View {
              Button {
                  incrementCardIndex()
              } label: {
                  ZStack {
                      CardPlaceholderView()
                      CardBackView()
                          .opacity(cards.isEmpty ? 0 : 1)
                  }
              }
              .buttonStyle(.plain)
              .disabled(cards.isEmpty)
              ZStack {
                  CardPlaceholderView()
                  if let currentCard {
                      CardFaceView(card: currentCard.value)
                          .draggable(containerItemID: currentCard.value)
                          .opacity(currentCard.value == hiddenCard ? 0 : 1)
                  }
              }
              .dragContainer(for: CardValue.self) { cardID in
                  [cardID]
              }
              // Add the drag configuration to allow me.
              .dragConfiguration(DragConfiguration(allowMove: true))
          }
      }
    • 12:05 - Add a drop destination modifier and configure it

      struct GameView: View {
          var game: Game
      
          var body: some View {
              GeometryReader { proxy in
                  let spacing: CGFloat = 10
                  let cardWidth = (proxy.size.width - 6 * spacing) / 7
                  VStack {
                      HStack(alignment: .top, spacing: spacing) {
                          Group {
                              RemainderView(game: game)
                              CardBackView()
                                  .hidden()
                              ForEach(CardValue.Suit.allCases) { suit in
                                  DestinationView(game: game, suit: suit)
                              }
                          }
                          .frame(width: cardWidth)
                      }
                      .padding(.bottom, 20)
                      HStack(alignment: .top, spacing: spacing) {
                          ForEach(0..<7) { index in
                              PileView(game: game, index: index)
                                  .frame(width: cardWidth)
                          }
                      }
                      .frame(maxHeight: .infinity, alignment: .top)
                      .reorderContainer(for: CardValue.self, in: Card.Group.self) { difference in
                          game.moveCards(difference: difference)
                      }
                      .dragContainer(for: CardValue.self) { cardID in
                          game.cardStack(startingAt: cardID)
                      }
                      .dragPreviewsFormation(.stack)
                      .dragConfiguration(DragConfiguration(allowMove: true))
                      // Add a drop destination to accept inserts
                      .dropDestination(for: CardValue.self) { newCards, session in
                          if let destination = session.reorderDestination(
                              for: CardValue.self, in: Card.Group.self) {
                              game.insertCards(newCards, to: destination)
                          }
                      }
                      // Configure where cards will go when reordering,
                      // and accept them by move.
                      .dropConfiguration { session in
                          // Calculate which pile is being dragged over.
                          let alignedX = session.location.x - 0.5 * spacing
                          let pile = Int(alignedX / (cardWidth + spacing))
                          let destination = ReorderDifference<CardValue, Card.Group>
                              .Destination(position: .end, collectionID: .pile(pile))
                          // Check if the move is allowed.
                          let allowed = session.suggestedOperations.contains(.move)
                          && game.validateMove(session: session, destination: destination)
                          let operation: DropOperation = allowed ? .move : .forbidden
                          return DropConfiguration(operation: operation, destination: destination)
                      }
                  }
                  .dropPreviewsFormation(.stack)
              }
              .padding()
          }
      }
    • 0:00 - Introduction
    • SwiftUI's expanded drag and drop in the 2027 releases — reorderable views, multi-item drags, and drag configuration — previewed through the Solitaire game used throughout the code-along.

    • 1:42 - Reordering
    • Adopt the new reorderable and reorderContainer modifiers to let people rearrange content with drag and drop. Demonstrated by enabling card reordering across all piles in a Solitaire app and excluding face-down cards from the interaction.

    • 6:50 - Drag multiple items
    • Use the drag container API to lift several items at once based on a selection. Customize how previews appear during the drag and at the drop destination with dragPreviewsFormation and dropPreviewsFormation — shown picking up and stacking multiple Solitaire cards.

    • 9:59 - Drag configuration
    • Express intent for how data transfers between a drag source and a drop destination. Use dragConfiguration to specify move (vs. copy) on the source, and dropConfiguration on the destination to have the final say — used to move a card from the deck into a pile without duplication.

    • 14:29 - Next steps
    • Recap: make your content reorderable, allow people to drag multiple items at once, and express intent with drag and drop configurations.

Developer Footer

  • ビデオ
  • WWDC26
  • Code Along:SwiftUIによるパワフルなドラッグ&ドロップ機能の構築
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • Apple Intelligence
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習とAI
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • Mini Apps Partner Program
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    最新ニュースを読む。
    Apple Developerアプリを入手する。
    Copyright © 2026 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン