SwiftUI: верхняя привязка изменяемых размеров представлений в представлении прокрутки - PullRequest
0 голосов
/ 04 октября 2019

Как я могу привязать изменяемые размеры представлений в виде прокрутки к верхней ведущей точке, чтобы при их увеличении они росли вниз?

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

Я не думаю, что проблема заключается в просмотре прокрутки, поскольку поведение будет таким же, если я заменю его на VStack. Однако в контексте просмотра прокрутки его изменение в сторону увеличения делает пользователя неспособным прокрутить вверх достаточно далеко, чтобы увидеть верхнюю часть представления.

Полный пример кода следует под скриншотами. Проблема возникает как на iPad, так и на симуляторе iPhone

. Первый снимок экрана показывает начальное состояние до изменения размера самого верхнего элемента

Start-state before resizing the topmost item

Второй снимок экрана показывает состояние после изменения размера самого верхнего элемента - самого верхнего элемента сейчасвыходит за пределы списка, поэтому мы не можем видеть прокрутку вверх, чтобы увидеть верхнюю часть

State after resizing the topmost item - the topmost item now goes outside the list so we cannot see scroll up to see the top

Здесь следует полный код, запускаемый с Xcode 11.0, чтобы показать проблему

struct ScaleItem: View {

    static let defaultHeight: CGFloat = 240.0

    @State var heightDiff: CGFloat = 0.0
    @State var currentHeight: CGFloat = ScaleItem.defaultHeight

    var resizingButton: some View {
        VStack {
            VStack {
                Spacer(minLength: 15)
                HStack {
                    Spacer()
                    Image(systemName: "arrow.up.and.down.square")
                        .background(Color.white)
                    Spacer()
                }
            }
            Spacer()
                .frame(height: 11)
        }
        .background(Color.clear)
    }

    var body: some View {

        ZStack {
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    Text("Sample")
                    Spacer()
                }
                Spacer()
            }
            .background(Color.red)
            .overlay(
                RoundedRectangle(cornerRadius: 5.0)
                    .strokeBorder(Color.black, lineWidth: 1.0)
                    .shadow(radius: 3.0)
            )
            .padding()
            .frame(
                minHeight: self.currentHeight + heightDiff,
                idealHeight: self.currentHeight + heightDiff,
                maxHeight: self.currentHeight + heightDiff,
                alignment: .top
            )

            resizingButton
                .gesture(
                    DragGesture()
                        .onChanged({ gesture in
                            print("Changed")
                            let location = gesture.location
                            let startLocation = gesture.startLocation
                            let deltaY = location.y - startLocation.y
                            self.heightDiff = deltaY
                            print(deltaY)
                        })
                    .onEnded { gesture in
                        print("Ended")
                        let location = gesture.location
                        let startLocation = gesture.startLocation
                        let deltaY = location.y - startLocation.y
                        self.currentHeight = max(ScaleItem.defaultHeight, self.currentHeight + deltaY)
                        self.heightDiff = 0
                        print(deltaY)
                        print(String(describing: gesture))
                    })

        }

    }
}

struct ScaleDemoView: View {

    var body: some View {

        ScrollView {
            ForEach(0..<3) { _ in
                ScaleItem()
            }
        }
    }

}

1 Ответ

1 голос
/ 05 октября 2019

Один из способов решения этой проблемы - перерисовать ScrollView в исходное положение во время перетаскивания. Создайте объект-наблюдатель, и когда ваш DeltaY будет изменен, родительское представление будет уведомлено, и представление обновится соответствующим образом.

  • Сначала создайте ObservableObject final class DeltaHeight: ObservableObject и перейдите к дочернему виду.
  • Добавьте .offset(x: 0, y: 0) в ScrollView

Вот проверенный код:

import SwiftUI

struct ScaleDemoView_Previews: PreviewProvider {
    static var previews: some View {
        ScaleDemoView()
    }
}

struct ScaleItem: View {
    @ObservedObject var delta: DeltaHeight

    static let defaultHeight: CGFloat = 200.0

    @State var heightDiff: CGFloat = 0.0
    @State var currentHeight: CGFloat = ScaleItem.defaultHeight

    var resizingButton: some View {
        VStack {
            VStack {
                Spacer(minLength: 15)
                HStack {
                    Spacer()
                    Image(systemName: "arrow.up.and.down.square")
                        .background(Color.white)
                    Spacer()
                }
            }
            Spacer()
                .frame(height: 11)
        }
        .background(Color.clear)
    }

    var body: some View {

        ZStack {
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    Text("Sample")
                    Spacer()
                }
                Spacer()
            }
            .background(Color.red)
            .overlay(
                RoundedRectangle(cornerRadius: 5.0)
                    .strokeBorder(Color.black, lineWidth: 1.0)
                    .shadow(radius: 3.0)
            )
            .padding()
            .frame(
                minHeight: self.currentHeight + heightDiff,
                idealHeight: self.currentHeight + heightDiff,
                maxHeight: self.currentHeight + heightDiff,
                alignment: .top
            )

            resizingButton
                .gesture(
                    DragGesture()
                        .onChanged({ gesture in
                            print("Changed")
                            let location = gesture.location
                            let startLocation = gesture.startLocation
                            let deltaY = location.y - startLocation.y
                            self.heightDiff = deltaY
                            print("deltaY: ", deltaY)
                            self.delta.delta = deltaY
                        })
                    .onEnded { gesture in
                        print("Ended")
                        let location = gesture.location
                        let startLocation = gesture.startLocation
                        let deltaY = location.y - startLocation.y
                        self.currentHeight = max(ScaleItem.defaultHeight, self.currentHeight + deltaY)
                        self.heightDiff = 0
                        print(deltaY)
                        print(String(describing: gesture))
                    })

        }

    }
}

struct ScaleDemoView: View {
    @ObservedObject var delta = DeltaHeight()
    var body: some View {
        ScrollView(.vertical) {
            ForEach(0..<3) { _ in
                ScaleItem(delta: self.delta)
            }
        }.offset(x: 0, y: 0)
            .background(Color.green)
    }

}

final class DeltaHeight: ObservableObject {
    @Published  var delta: CGFloat = 0.0
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...