ScrollView frame в SwiftUI


#1

Ребята, привет. Ситуация до банальности простейшая (а я начинающий), но никто не может решить этот вопрос даже на stackoverflow. Последняя надежда на вас! :slight_smile:
У меня внутри ScrollView расположен стек с картинками (ForEach). За картинку можно потянуть, и при отпускании она “летит” в другой массив (“на стол”). Так вот, из-за того, что картинки лежат в ScrollView, анимация перетягивания отображается лишь в рамках этого самого ScrollView, а за его рамками - пустота.
04
Если же ScrollView убрать, оставив лишь HStack, то всё идёт, как надо.

Вопрос: как пофиксить эту невиданную хрень?
Код выгдялит как-то так в общих чертах:
ScrollView(.horizontal) {
HStack(alignment: .center, spacing: 0) {
ForEach (player.playersCards, id: .self.id) { card in
Image(card.pic)
.resizable()
.frame(width: 93, height: 127)
.modifier(CardStyle())
.matchedGeometryEffect(id: card.id, in: dealingNamespace)
.offset(dragOffset[card.id])
.gesture(
DragGesture()


#2

Может проще создать дубликат во время перетаскивания ( основной делать прозрачным )
дубликат положить на основной слой и перетаскивать
Как только отпускаем - создается новый на столе и копия уничтожается


#3

Интересная мысль! Завтра попробую. Спасибо.


#4
ScrollView(.horizontal) {
    ...
}
.frame(height: UIScreen.main.bounds.height)

не?

Размер родительского фрейма в иерархии определяется дочерними элементами, поэтому у вас высота скроллвью равна дочерней высоте. Для решения достаточно принудительно определить высоту нужного фрейма. Код не проверял, но должно работать.

ПС: если у вас применяется несколько ориентаций, то при переворачивание с UIScreen.main.bounds.height естественно всё поломается. В таком случае высоту (если на весь экран надо) надо брать из GeometryReader.

ППС: скиньте ссылку на вопрос на стэке - просто интересно как вы задали вопрос, ведь ситуация вроде плёвая


#5

Дошли руки наконец до этой проблемы. В общем, вариант с указанием высоты фрейма проблему не решает. Всё остаётся, как было. Вот так сформулировал вопрос на стеке - https://stackoverflow.com/questions/66941258/scrollview-limits-draggesture-animation . Ответа никто не знает. Мне Psilc отсюда прислал демку на UIKit весьма замороченную, но там при запуске в эмуляторе вообще ничего не перетаскивается. Короче, странная история. Я в курсе, что можно как-то сделать “долгий клик” и потом тащить (я это видел в демке Стэнфордского Универа EmojiArt), но это не то, нужно без долгих кликов сразу тянуть за границы скроллвью…


#6

А вы смотрели как делается drag&drop в UIKit через CollectionView?
Там когда ячейка срабатывает на тап, создается копия, а оригинал прячется, либо становится полупрозрачным. Дальше работа идет уже только с копией.
Понятия не имею как это делается на SwiftUI.


#7

ВОООО. Сюда надо было тоже сразу код выложить - всё сразу стало ясно )))
Вам нужно просто упаковать ваш имидж с картой в другое вью (группу или ЗиСтек)! Задать высоту этому вью в высоту экрана, тогда карта будет “ездить” внутри этого вью - собственно это я вам и предлагал:

import SwiftUI

struct SwiftUIView: View {
    
    private let colors = [Color.blue, Color.yellow, Color.orange, Color.gray, Color.black, Color.green, Color.white]
    
    @State var position = CGSize.zero
    @GestureState var dragOffset: [CGSize]

        init() {
            let  dragOffsets = [CGSize](repeating: CGSize.zero, count: 36)
            _dragOffset = GestureState(wrappedValue: dragOffsets)
        }
    
    var body: some View {
        
        ScrollView(.horizontal) {
            HStack(alignment: .center, spacing: 0) {
                ForEach (Array(0...6), id: \.self) { number in
                    Group {
                        Text("\(number.description)")
                            .frame(width: 93, height: 127)
                            .background(colors[number])
                            .offset(dragOffset[number])
                        .gesture(
                            DragGesture(coordinateSpace: .global)
                                .updating($dragOffset, body: { (value, state, transaction) in
                                    state[number] = value.translation

                                })
                        )
                        .animation(.spring())
                    }
                    .frame(width: 93, height: UIScreen.main.bounds.height)
                }
            }
        }
        .background(Color.red)
    }
}


#8

Благодарю! Отличное решение. Только я вставил в вертикальный стек, так как мне нужно держать карты в нижней части экрана.