

swiftuiを使ってiOS用のアプリを作成しているのですが、不明な点がありましたら教えてください。あるスクロールビューに表示されている画像をドラッグして別のスクロールビューに移動したいのですが、現在の問題は、画像をドラッグして移動すると、画像がスクロールビューの外に出ると表示されず、移動できないことです。別のスクロールビューに移動してください。この問題の解決方法を教えていただければ幸いです。

[code]

    import SwiftUI
    
    struct ContentView: View {
        @State private var pageOffsets: [CGSize] = Array(repeating: .zero, count: 50)
        @State private var pageGridColumns: [Int] = Array(repeating: 0, count: 50)
        let grids = Array(repeating: GridItem(.fixed(80)), count: 4)
        
        var body: some View {
            LazyVStack {
                ScrollView() {
                    LazyHStack {
                        LazyVGrid(columns: grids) {
                            ForEach((1...50), id: \.self) { num in
                                Page(str: String(num))
                                    .cornerRadius(8)
                                    .frame(height: 60)
                                    .offset(pageOffsets[num - 1])
                                    .gesture(
                                        DragGesture()
                                            .onChanged { value in
                                                pageOffsets[num - 1] = value.translation
                                            }
                                            .onEnded { value in
                                                let columnIndex = Int(round(value.translation.width / 80))
                                                let newOffset = CGSize(width: CGFloat(columnIndex) * 80, height: 0)
                                                withAnimation {
                                                    pageOffsets[num - 1] = newOffset
                                                    pageGridColumns[num - 1] = columnIndex
                                                }
                                            }
                                    )
                                    .onAppear {
                                        pageGridColumns[num - 1] = (num - 1) % 4
                                    }
                            }
                        }
                    }
                }
                ScrollView() {
                    LazyHStack {
                        LazyVGrid(columns: grids) {
                            ForEach((1...50), id: \.self) { num in
                                Page(str: String(num))
                                    .cornerRadius(8)
                                    .frame(height: 60)
                                    .offset(pageOffsets[num - 1])
                                    .gesture(
                                        DragGesture()
                                            .onChanged { value in
                                                pageOffsets[num - 1] = value.translation
                                            }
                                            .onEnded { value in
                                                let columnIndex = Int(round(value.translation.width / 80))
                                                let newOffset = CGSize(width: CGFloat(columnIndex) * 80, height: 0)
                                                withAnimation {
                                                    pageOffsets[num - 1] = newOffset
                                                    pageGridColumns[num - 1] = columnIndex
                                                }
                                            }
                                    )
                                    .onAppear {
                                        pageGridColumns[num - 1] = (num - 1) % 4
                                    }
                            }
                        }
                    }
                }
            }
        }
    }
    
    struct Page: View {
        let colors: [Color] = [.green, .blue, .pink, .orange, .purple]
        let str: String
    
        var body: some View {
            ZStack {
                colors.randomElement()
                Text(str)
                    .font(.title)
                    .foregroundColor(.white)
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2023-08-26T16:52:11.780
96053
2023-09-01T16:49:11.127
59585
question
swift ios swiftui
スクロールビュー間で画像を移動する方法


SwiftUIのdraggable, dropDestinationを使うとgestureを気にせずに実装することができます。

<https://zenn.dev/zunda_pixel/articles/7dd3a47b0a3998>

YouTubeに1つのScrollViewですが同じような要件のものがあります。これを参考にすればいけると思います。

<https://www.youtube.com/watch?v=UFiOCcm6zTo>

長いですが、一応動くコードも置いておきます。

[code]

    import SwiftUI
    import CoreTransferable
    
    struct ContentView: View {
      @State private var topPageItems: [PageItem] = (0..<20).map { i in
          .init(str: String(i))
      }
      
      @State private var bottomPageItems: [PageItem] = (0..<20).map { i in
          .init(str: String(i))
      }
      
      @State private var draggingItem: PageItem?
      let columns = Array(repeating: GridItem(spacing: 10), count: 3)
    
      var body: some View {
        VStack {
          ScrollView(.vertical) {
            
            LazyVGrid(columns: columns, spacing: 10) {
              ForEach(self.topPageItems) { pageItem in
                PageView(page: pageItem)
                  .id(pageItem.id)
                  .draggable(pageItem) {
                    PageView(page: pageItem)
                      .id(pageItem.id)
                      .frame(width: 100, height: 50)
                      .onAppear {
                        self.draggingItem = pageItem
                      }
                  }
                  .dropDestination(for: PageItem.self) { items, location in
                    self.draggingItem = nil
                    return false
                  } isTargeted: { status in
                    guard let draggingItem, status, draggingItem.id != pageItem.id else { return }
                    if let sourceIndex = self.topPageItems.firstIndex(where: { $0.id ==  draggingItem.id }) {
                      let destinationIndex = self.topPageItems.firstIndex { $0.id == pageItem.id }
                      guard let destinationIndex else { return }
                      
                      withAnimation(.bouncy) {
                        let sourceItem = self.topPageItems.remove(at: sourceIndex)
                        self.topPageItems.insert(sourceItem, at: destinationIndex)
                      }
                    } else if let sourceIndex = self.bottomPageItems.firstIndex(where: { $0.id ==  draggingItem.id }) {
                      let destinationIndex = self.topPageItems.firstIndex { $0.id == pageItem.id }
                      guard let destinationIndex else { return }
                      
                      withAnimation(.bouncy) {
                        let sourceItem = self.bottomPageItems.remove(at: sourceIndex)
                        self.topPageItems.insert(sourceItem, at: destinationIndex)
                      }
                    }
                  }
              }
            }
            .padding(15)
          }
          
          ScrollView(.vertical) {
            LazyVGrid(columns: columns, spacing: 10) {
              ForEach(self.bottomPageItems) { pageItem in
                PageView(page: pageItem)
                  .id(pageItem.id)
                  .draggable(pageItem) {
                    PageView(page: pageItem)
                      .id(pageItem.id)
                      .frame(width: 100, height: 50)
                      .onAppear {
                        self.draggingItem = pageItem
                      }
                  }
                  .dropDestination(for: PageItem.self) { items, location in
                    self.draggingItem = nil
                    return false
                  } isTargeted: { status in
                    guard let draggingItem, status, draggingItem.id != pageItem.id else { return }
                    if let sourceIndex = self.bottomPageItems.firstIndex(where: { $0.id ==  draggingItem.id }) {
                      let destinationIndex = self.bottomPageItems.firstIndex { $0.id == pageItem.id }
                      guard let destinationIndex else { return }
                      
                      withAnimation(.bouncy) {
                        let sourceItem = self.bottomPageItems.remove(at: sourceIndex)
                        self.bottomPageItems.insert(sourceItem, at: destinationIndex)
                      }
                    } else if let sourceIndex = self.topPageItems.firstIndex(where: { $0.id ==  draggingItem.id }) {
                      let destinationIndex = self.bottomPageItems.firstIndex { $0.id == pageItem.id }
                      guard let destinationIndex else { return }
                      
                      withAnimation(.bouncy) {
                        let sourceItem = self.topPageItems.remove(at: sourceIndex)
                        self.bottomPageItems.insert(sourceItem, at: destinationIndex)
                      }
                    }
                  }
              }
            }
            .padding(15)
          }
        }
      }
    }
    
    #Preview {
      ContentView()
    }
    
    
    import UniformTypeIdentifiers
    
    struct PageItem: Identifiable, Codable, Transferable {
      let id: UUID
      let str: String
      let color: Color
      
      init(str: String) {
        self.id = UUID()
        self.str = str
        let allColors: [Color] = [.green, .blue, .pink, .orange, .purple]
        self.color = allColors.randomElement()!
      }
      
      static var transferRepresentation: some TransferRepresentation {
        CodableRepresentation(for: PageItem.self, contentType: .content)
      }
    }
    
    struct PageView: View {
      let page: PageItem
      
      var body: some View {
        ZStack {
          page.color
          Text(page.str)
            .font(.title)
            .foregroundColor(.white)
        }
      }
    }
    
    extension Color: Codable {
      enum CodingKeys: String, CodingKey {
        case red
        case green
        case blue
        case opacity
      }
      
      public func encode(to encoder: Encoder) throws {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var opacity: CGFloat = 0
        
        var container = encoder.container(keyedBy: CodingKeys.self)
        UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: &opacity)
        try container.encode(red, forKey: .red)
        try container.encode(green, forKey: .green)
        try container.encode(blue, forKey: .blue)
        try container.encode(opacity, forKey: .opacity)
      }
      
      public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let red = try container.decode(Double.self, forKey: .red)
        let green = try container.decode(Double.self, forKey: .green)
        let blue = try container.decode(Double.self, forKey: .blue)
        let opacity = try container.decode(Double.self, forKey: .opacity)
        self.init(red: red, green: green, blue: blue, opacity: opacity)
      }
    }
    
[/code]


CC BY-SA 4.0
2023-09-01T16:25:25.403
96099
2023-09-01T16:49:11.127
2023-09-01T16:49:11.127
40856
40856
96053
answer