สร้าง 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)
    }
}

สิ่งนี้อธิบายว่าเป็นกรอบที่ยืดหยุ่นได้ ( ดูเอกสารประกอบ ) ซึ่งจะยืดออกจนเต็มหน้าจอและเมื่อมีพื้นที่เพิ่มขึ้นก็จะจัดกึ่งกลางเนื้อหาไว้ด้านใน


subviewsไม่จัดไปซ้าย ( .leading) ใช้ความกว้างเต็มหากวางไว้ก่อน.background(Color.red)
ielyamani

เพิ่มโค้ดให้ชิดซ้ายเช่นกัน ควรจะตรงกับตอนนี้
svarrall

12
นี่เป็นสิ่งที่ไม่ถูกต้อง .backgound(Color.red)เป็นพื้นหลังของมุมมองกรอบ แต่ไม่ได้พื้นหลังของVStackที่อยู่ในมุมมองกรอบ (เหตุผลนี้มีอธิบายไว้ในวิดีโอ WWDC: P) หากคุณวางไว้ข้าง.background(Color.green)หน้า.frameคุณจะเห็นVStackภาพนิ่งมีความกว้างที่พอดีกับเนื้อหาในขณะที่กรอบมีพื้นหลังสีแดง ... เมื่อคุณวาง.frame(...)มันไม่ได้แก้ไขVStackแต่เป็นการสร้างมุมมองอื่นขึ้นมา
Jake

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
ielyamani

1
ฉันพบว่าสิ่งนี้ทั้งใช้งานง่ายและสอดคล้องกับคำถามของ OP มากขึ้นซึ่งฉันคิดว่าจะทำอย่างไรให้องค์ประกอบ Stack เติมเต็มคอนเทนเนอร์มากกว่าวิธีการแทรกคอนเทนเนอร์รอบ ๆ องค์ประกอบ
brotskydotcom

1
ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดที่สอดคล้องกับแนวคิดเบื้องหลัง SwiftUI
krummens

1
คำตอบ SwiftUI-y ที่ชัดเจนที่สุดที่นี่สำหรับรูปแบบทั่วไปดังกล่าว
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มี a Spacer()as it's ลงในไฟล์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)
ielyamani

ฉันไม่เห็นGeometryReaderในรายการที่แสดงเมื่อฉัน "เพิ่มตัวปรับแต่ง" หลังจากฉันShow SwiftUI Inspectorหรือที่อื่น ๆ ภายใน UI คุณค้นพบได้GeometryReaderอย่างไร?
หายไป

1
@gone - Stack Overflow :)
Jake

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และใช้การต่อท้ายและด้านล่างSpacerเพื่อบังคับให้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. ใช้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
Imho นี่คือทางออกที่สวยงามที่สุด
Vyacheslav

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

ฉันรู้ว่าสิ่งนี้จะไม่ได้ผลสำหรับทุกคน แต่ฉันคิดว่ามันน่าสนใจที่เพียงแค่เพิ่ม Divider แก้ปัญหานี้

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.