Пространство по умолчанию в SwiftUI ScrollView

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

Вот как я это реализую:

struct CustomVerticalScrollView<Content: View>: View {
    let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        ScrollView {
            content
            
            Color
                .clear
                .frame(width: 0, height: 0)
                .modifier(ViewAppearsOnScreen())
                .onPreferenceChange(IsOnScreenKey.self) { value in
                    if value == true {
                        print("Has reached end!")
                    }
                }
        }
        .background(.green)
    }
}

struct ViewAppearsOnScreen: ViewModifier {
    func body(content: Content) -> some View {
        GeometryReader { geometry in
            // Check if this view's frame intersects with the visible screen bounds
            content
                .preference(
                    key: IsOnScreenKey.self,
                    value: isViewVisible(geometry: geometry)
                )
        }
    }
    
    // Determines if the view is visible within the screen's bounds
    private func isViewVisible(geometry: GeometryProxy) -> Bool {
        let frame = geometry.frame(in: .global) // Get the frame in global space
        let screenBounds = UIScreen.main.bounds // Screen boundaries
        
        return screenBounds.intersects(frame) // Check if it intersects with screen bounds
    }
}

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

И вот как я его использую:

struct ContentView: View {
    var body: some View {
        
        CustomVerticalScrollView{
            VStack(spacing: .zero) {
                Color
                    .blue
                    .frame(maxWidth: .infinity)
                    .frame(height: 1000)
            }
        }
    }
}

Однако эта функциональность работает хорошо. Это дает мне следующие результаты с некоторым дополнительным интервалом:

  1. Когда у меня нет четкого цветового представления, кажется, что в конце не добавляется пробел, даже если нет отступов, интервалов и высота представления равна 0 (третий элемент в таблице).
  2. Когда я добавляю четкое цветовое представление, в конце добавляется значительный интервал (первый элемент в таблице).
  3. Если я удалю два упомянутых модификатора из второго элемента таблицы, некоторые отступы все равно останутся - вероятно, здесь работает программа чтения геометрии.

Хотя я установил высоту на 0 и использовал программу чтения геометрии, это не повлияет на интервал.

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

Попробуйте поставить frame(width: 0, height: 0) после modifier и onPreferenceChange и окружить content и Color.clearVStack(spacing: .zero), возможно?

Sweeper 16.05.2024 09:46

Кажется, это действительно работает @Sweeper. Я немного понимаю, как добавить кадр после всех модификаторов, но не понимаю, зачем нам нужен VStack с нулевым интервалом. Кажется, что между элементами в режиме прокрутки существует какое-то расстояние по умолчанию, которое мы не можем переопределить?

Shawn Frank 16.05.2024 09:54

Да, действительно существует «некоторое расстояние по умолчанию между элементами в режиме прокрутки». На самом деле вы можете прочитать, насколько велик этот интервал, реализуя свой собственный Layout. См. LayoutSubview.spacing.

Sweeper 16.05.2024 09:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

GeometryReader в этом случае изменит свой размер до идеальной высоты, которая оказывается небольшим ненулевым значением. Вы можете переместить frame(width: 0, height: 0) после onPreferenceChange, чтобы это исправить.

Каждое представление SwiftUI имеет свой собственный «предпочтительный интервал» по всем четырем краям, и этот интервал может различаться в зависимости от типа другого представления. Например, предпочтительное расстояние между Text и Image немного больше, чем предпочтительное расстояние между двумя Text. Поведение макета по умолчанию учитывает это предпочтение интервала. При реализации собственных Layout вы можете прочитать это предпочтительное расстояние, используя LayoutSubview.spacing.

Использование VStack с явным параметром spacing: приводит к тому, что макет VStack не учитывает предпочтение интервала.

ScrollView {
    VStack(spacing: 0) {
        content
        Color
            .clear
            .modifier(ViewAppearsOnScreen())
            .onPreferenceChange(IsOnScreenKey.self) { ... }
            .frame(width: 0, height: 0)
    }
}

Другие вопросы по теме