Зробіть VStack заповненням ширини екрана в SwiftUI


119

Враховуючи цей код:

import SwiftUI

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)

            Text("Content")
                .lineLimit(nil)
                .font(.body)

            Spacer()
        }
        .background(Color.red)
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

Це призводить до такого взаємодії:

попередній перегляд

Як я можу зробити VStackзаповнення шириною екрану, навіть якщо мітки / текстові компоненти не потребують повної ширини?

Трюк, який я знайшов, полягає в тому, щоб вставити порожнє HStack в структуру так:

VStack(alignment: .leading) {
    HStack {
        Spacer()
    }
    Text("Title")
        .font(.title)

    Text("Content")
        .lineLimit(nil)
        .font(.body)

    Spacer()
}

Що дає бажаний дизайн:

бажаний результат

Чи є кращий спосіб?


1
Зверніться до мого відповіді, яке показує до 5 альтернативних способів отримати подібний результат.
Imanou Petit

Відповіді:


199

Спробуйте скористатися модифікатором .frame з такими параметрами:

.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Hello World").font(.title)
            Text("Another").font(.body)
            Spacer()
        }.frame(minWidth: 0,
                maxWidth: .infinity,
                minHeight: 0,
                maxHeight: .infinity,
                alignment: .topLeading
        ).background(Color.red)
    }
}

Це описується як гнучкий кадр ( див. Документацію ), який розтягується, щоб заповнити весь екран, а коли у нього є додатковий простір, він відцентрує свій вміст усередині нього.


У підвиди НЕ вирівнюють вліво ( .leading). Бере на повну ширину, якщо розміщений раніше.background(Color.red)
ієлямані

Додано код для вирівнювання також ліворуч. Має збігатися точно зараз
svarrall

12
Це неправильно. Це .backgound(Color.red)- фон перегляду кадру, але не фон того, VStackщо знаходиться у поданні кадру. (Причина цього пояснюється у відеозаписах WWDC: P). Якщо ви ставите .background(Color.green)перед, .frameви побачите, що VStackкадр все ще має ширину, яка відповідає його вмісту, тоді як кадр має червоний фон ... Коли ви ставите .frame(...), це не редагування VStack, це фактично створення іншого перегляду.
Джейк,

2
@Jake здається рецепт для катастрофи
Sudara

1
Є запитання, чому: навіщо використовувати alignment: .topLeading, оскільки ми маємо VStack (alignment: .leading) ?? Дякуємо за обмін
UIChris

36

Альтернативна схема укладання, яка працює і, можливо, трохи інтуїтивніша, полягає в наступному:

struct ContentView: View {
    var body: some View {
        HStack() {
            VStack(alignment: .leading) {
                Text("Hello World")
                    .font(.title)
                Text("Another")
                    .font(.body)
                Spacer()
            }
            Spacer()
        }.background(Color.red)
    }
}

Вміст також можна легко переставити, видаливши Spacer()символи, якщо це необхідно.

додавання / видалення розпірок для зміни положення


5
Хороша альтернатива 👍🏻Сподобався gif
ієлямані

1
Я вважаю це одночасно більш інтуїтивним та більш узгодженим із запитанням OP, яким я вважав, як змусити елемент Stack заповнювати свій контейнер, а не як вставляти контейнер навколо елемента.
brotskydotcom

1
Я вважаю, що це найкраща відповідь, яка узгоджується з ідеєю SwiftUI
krummens

1
Безумовно, найбільш швидка відповідь тут на такий поширений макет.
dead_can_dance

30

Є кращий спосіб!

Щоб зробити VStackзаливку шириною батьківської, ви можете використовувати a GeometryReaderта встановити кадр. ( .relativeWidth(1.0)має працювати, але зараз, мабуть, не працює)

struct ContentView : View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("test")
            }
                .frame(width: geometry.size.width,
                       height: nil,
                       alignment: .topLeading)
        }
    }
}

Щоб встановити VStackширину фактичного екрану, ви можете використовувати його UIScreen.main.bounds.widthпри встановленні кадру замість використання a GeometryReader, але, я думаю, ви, мабуть, хотіли ширину батьківського подання.

Крім того , цей спосіб має додаткову перевагу, не додаючи інтервал в вашому VStackякий може статися (якщо інтервал) , якщо ви додавали HStackз , Spacer()як його зміст до VStack.

ОНОВЛЕННЯ - НЕ КРАЩОГО ШЛЯХУ!

Перевіривши прийняту відповідь, я зрозумів, що прийнята відповідь насправді не працює! Здається, це працює на перший погляд, але якщо ви оновите, VStackщоб мати зелений фон, ви помітите, що VStackце все та ж ширина.

struct ContentView : View {
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                Text("Hello World")
                    .font(.title)
                Text("Another")
                    .font(.body)
                Spacer()
                }
                .background(Color.green)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
                .background(Color.red)
        }
    }
}

Це пов’язано з тим .frame(...), що насправді додається інший вигляд до ієрархії подання, і цей вигляд в кінцевому підсумку заповнює екран. Однак VStackвсе ще немає.

Це питання також схоже на те ж саме в моїй відповіді, і його можна перевірити за тим же підходом, що і вище (нанесення різних кольорів тла до і після .frame(...). Єдиний спосіб, який, здається, насправді розширює - VStackце використання розпірок:

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack{
                Text("Title")
                    .font(.title)
                Spacer()
            }
            Text("Content")
                .lineLimit(nil)
                .font(.body)
            Spacer()
        }
            .background(Color.green)
    }
}

Спробував relativeWidth, не працював. Якось GeometryReaderспрацьовує, якщо .frameпоміщений раніше.background(Color.red)
ієлямані

Я не бачу GeometryReaderу списку, що відображається, коли я "Додати модифікатор" після I Show SwiftUI Inspector, або де-небудь ще в інтерфейсі. Як ви виявили GeometryReader?
пішов

1
@gone - Переповнення стека :)
Джейк

20

За допомогою Swift 5.2 та iOS 13.4, відповідно до ваших потреб, ви можете скористатися одним із наведених нижче прикладів, щоб узгодити свої VStackверхні обмеження та рамку в повному розмірі.

Зверніть увагу, що фрагменти коду нижче всіх призводять до одного і того ж відображення, але не гарантують ефективного кадру VStackні кількості Viewелементів, які можуть з'явитися під час налагодження ієрархії подання.


1. Використання frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)методу

Найпростіший підхід - це встановити рамку вашого VStackз максимальною шириною та висотою, а також передати необхідне вирівнювання в frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:):

struct ContentView: View {

    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)
            Text("Content")
                .font(.body)
        }
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity,
            alignment: .topLeading
        )
        .background(Color.red)
    }

}

Як альтернативу, якщо встановлення максимального кадру з конкретним вирівнюванням для вашого Views є загальним шаблоном у вашій основі коду, ви можете створити Viewдля нього метод розширення :

extension View {

    func fullSize(alignment: Alignment = .center) -> some View {
        self.frame(
            maxWidth: .infinity,
            maxHeight: .infinity,
            alignment: alignment
        )
    }

}

struct ContentView : View {

    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)
            Text("Content")
                .font(.body)
        }
        .fullSize(alignment: .topLeading)
        .background(Color.red)
    }

}

2. Використання Spacers для примусового вирівнювання

Ви можете вставити VStackвсередину повний розмір HStackі скористатися кінцевим та нижнім Spacers, щоб змусити VStackверхнє вирівнювання:

struct ContentView: View {

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)

                Spacer() // VStack bottom spacer
            }

            Spacer() // HStack trailing spacer
        }
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity
        )
        .background(Color.red)
    }

}

3. Використання a ZStackта повнорозмірного фонуView

Цей приклад показує, як вставити VStackвсередину все, ZStackщо має вирівнювання по верхній частині. Зверніть увагу, як Colorподання використовується для встановлення максимальної ширини та висоти:

struct ContentView: View {

    var body: some View {
        ZStack(alignment: .topLeading) {
            Color.red
                .frame(maxWidth: .infinity, maxHeight: .infinity)

            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)
            }
        }
    }

}

4. Використання GeometryReader

GeometryReaderмає таку декларацію :

Перегляд контейнера, що визначає його вміст як функцію власного розміру та простору координат. [...] Цей подання повертає гнучкий бажаний розмір своєму батьківському макету.

Фрагмент коду нижче показує, як використовувати його GeometryReaderдля узгодження VStackз провідними обмеженнями та повнорозмірним фреймом:

struct ContentView : View {

    var body: some View {
        GeometryReader { geometryProxy in
            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)
            }
            .frame(
                width: geometryProxy.size.width,
                height: geometryProxy.size.height,
                alignment: .topLeading
            )
        }
        .background(Color.red)
    }

}

5. Використання overlay(_:alignment:)методу

Якщо ви хочете вирівняти свої VStackнайвищі обмеження до існуючого повного розміру View, ви можете використовувати overlay(_:alignment:)метод:

struct ContentView: View {

    var body: some View {
        Color.red
            .frame(
                maxWidth: .infinity,
                maxHeight: .infinity
            )
            .overlay(
                VStack(alignment: .leading) {
                    Text("Title")
                        .font(.title)
                    Text("Content")
                        .font(.body)
                },
                alignment: .topLeading
            )
    }

}

Дисплей:


10

Найпростіший спосіб вирішити проблему - це використання ZStack + .edgesIgnoringSafeArea (.all)

struct TestView : View {

    var body: some View {
        ZStack() {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("Hello World")
            }
        }
    }
}


4

Ви можете зробити це за допомогою GeometryReader

GeometryReader

Код:

struct ContentView : View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
               Text("Turtle Rock").frame(width: geometry.size.width, height: geometry.size.height, alignment: .topLeading).background(Color.red)
            }
        }
    }
}

Ваш результат, як:

введіть тут опис зображення


4

Хорошим рішенням і без "вигадок" є забуте ZStack

ZStack(alignment: .top){
    Color.red
    VStack{
        Text("Hello World").font(.title)
        Text("Another").font(.body)
    }
}

Результат:

введіть тут опис зображення


3

Ще однією альтернативою є розміщення однієї з підпрограм всередині HStackі розміщення Spacer()після неї:

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {

            HStack {
                Text("Title")
                    .font(.title)
                    .background(Color.yellow)
                Spacer()
            }

            Text("Content")
                .lineLimit(nil)
                .font(.body)
                .background(Color.blue)

            Spacer()
            }

            .background(Color.red)
    }
}

в результаті:

HStack всередині VStack


1
Імхо, це найелегантніше рішення
В’ячеслав

3

Це те, що в мене працювало ( ScrollView(необов’язково), тому за потреби можна додати більше вмісту, а також вміст у центрі):

import SwiftUI

struct SomeView: View {
    var body: some View {
        GeometryReader { geometry in
            ScrollView(Axis.Set.horizontal) {
                HStack(alignment: .center) {
                    ForEach(0..<8) { _ in
                        Text("🥳")
                    }
                }.frame(width: geometry.size.width, height: 50)
            }
        }
    }
}

// MARK: - Preview

#if DEBUG
struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        SomeView()
    }
}
#endif

Результат

по центру


2

Я знаю, що це не буде працювати для всіх, але мені здалося цікавим, що просто додавання розділювача вирішує це.

struct DividerTest: View {
var body: some View {
    VStack(alignment: .leading) {
        Text("Foo")
        Text("Bar")
        Divider()
    }.background(Color.red)
  }
}

2

Це корисний біт коду:

extension View {
    func expandable () -> some View {
        ZStack {
            Color.clear
            self
        }
    }
}

Порівняйте результати з .expandable()модифікатором і без нього :

Text("hello")
    .background(Color.blue)

-

Text("hello")
    .expandable()
    .background(Color.blue)

введіть тут опис зображення


2

введіть тут опис зображення

Дизайн сторінки входу за допомогою SwiftUI

import SwiftUI

struct ContentView: View {

    @State var email: String = "william@gmail.com"
    @State var password: String = ""
    @State static var labelTitle: String = ""


    var body: some View {
        VStack(alignment: .center){
            //Label
            Text("Login").font(.largeTitle).foregroundColor(.yellow).bold()
            //TextField
            TextField("Email", text: $email)
                .textContentType(.emailAddress)
                .foregroundColor(.blue)
                .frame(minHeight: 40)
                .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.green))

            TextField("Password", text: $password) //Placeholder
                .textContentType(.newPassword)
                .frame(minHeight: 40)
                .foregroundColor(.blue) // Text color
                .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.green))

            //Button
            Button(action: {

            }) {
                HStack {
                    Image(uiImage: UIImage(named: "Login")!)
                        .renderingMode(.original)
                        .font(.title)
                        .foregroundColor(.blue)
                    Text("Login")
                        .font(.title)
                        .foregroundColor(.white)
                }


                .font(.headline)
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(LinearGradient(gradient: Gradient(colors: [Color("DarkGreen"), Color("LightGreen")]), startPoint: .leading, endPoint: .trailing))
                .cornerRadius(40)
                .padding(.horizontal, 20)

                .frame(width: 200, height: 50, alignment: .center)
            }
            Spacer()

        }.padding(10)

            .frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity, minHeight: 0, idealHeight: .infinity, maxHeight: .infinity, alignment: .top)
            .background(Color.gray)

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

1

Ви можете використовувати GeometryReader у зручному розширенні для заповнення батьків

extension View {
    func fillParent(alignment:Alignment = .center) -> some View {
        return GeometryReader { geometry in
            self
                .frame(width: geometry.size.width,
                       height: geometry.size.height,
                       alignment: alignment)
        }
    }
}

отже, використовуючи запитаний приклад, ви отримуєте

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)

            Text("Content")
                .lineLimit(nil)
                .font(.body)
        }
        .fillParent(alignment:.topLeading)
        .background(Color.red)
    }
}

(зверніть увагу, проставка більше не потрібна)

введіть тут опис зображення


це не на весь екран.
Качка

питання не вимагало повноекранного режиму, воно вимагало повної ширини. Якщо ви хочете пройти повз безпечну зону, тоді використовуйте. edgeIgnoringSafeArea
Confused Vorlon

0
 var body: some View {
      VStack {
           CarouselView().edgesIgnoringSafeArea(.all)           
           List {
                ForEach(viewModel.parents) { k in
                    VideosRowView(parent: k)
                }
           }
    }
 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.