SwiftUI растягивается шире экрана с помощью alignmentGuide для просмотра в другом контейнере вида - PullRequest
1 голос
/ 01 апреля 2020

Я пытаюсь изучить SwiftUI, и я прочитал эту действительно замечательную статью о alignmentGuides и о том, как выровнять представления, которые находятся в разных контейнерах: https://swiftui-lab.com/alignment-guides/

В моем пользовательском интерфейсе я хочу иметь галочку, затем вопрос с меткой Text () в одной строке (оранжевый), затем ниже, где я хочу TextField (), где они могут ответить на вопрос (зеленый), и я хочу, чтобы TextField () приводил к совпадать с лидирующим вопросом Text (). Вот так:

example of what I want

Приведенный выше снимок экрана был создан, когда я принудительно установил ширину зеленого TextField () с помощью .frame(width: 200). Я не хочу, чтобы текстовое поле было определенного размера, но когда я

К сожалению, всякий раз, когда я удаляю .frame(width: 200) ...

1) зеленый TextField () тогда растягивается шире экрана 2) внешний VStack также простирается за пределы экрана. 3) и галочка Image () на оранжевом HStack выровнена по передней стороне экрана.

Вот как это выглядит без TextField().frame(width: 200), обратите внимание, что синий контур VStack выключен обе стороны экрана:

screenshot of the problem layout

Вот код:

extension HorizontalAlignment {
    private enum QuestionAlignment : AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            return d[.leading]
        }
    }

    static let questionAlignment = HorizontalAlignment(QuestionAlignment.self)
}

struct EasyModeQuestion: View {

    @Binding var field : String

    var body: some View {
        VStack(alignment: .questionAlignment) {
            Rectangle()
                .fill(Color.primary)
                .frame(width: 1)
                .alignmentGuide(.questionAlignment, computeValue: { d in d[HorizontalAlignment.leading] })

            HStack() {
                Image(systemName: "checkmark.square")

                Text("What is the blah blah blah?")
                    .alignmentGuide(.questionAlignment, computeValue: { d in d[.leading] })

            }.background(Color.orange)


            TextFieldRoundedRect(placeholder: "", text: $field)
                .alignmentGuide(.questionAlignment, computeValue: { d in d[.leading] })
                .background(Color.green)
                //.frame(width: 200)


            Rectangle()
                .fill(Color.primary)
                .frame(width: 1)
                .alignmentGuide(.questionAlignment, computeValue: { d in d[HorizontalAlignment.leading] })
        }

    }
}

Два объекта Rectangle () сверху и снизу Приведены примеры из ссылки в верхней части моего вопроса, которая должна помочь визуализировать, где находится руководство по выравниванию.

Когда я просто go с базовым c ведущим выравниванием и удаляю все направляющие выравнивания, тогда TextField () возвращается к соответствующей ширине:

enter image description here

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Я автор статьи, на которую вы ссылаетесь в начале вашего вопроса. Согласия приходят со своими проблемами, не так ли? В вашем случае позвольте мне предложить другой подход. Я знаю, что для многих этот вариант может показаться дешевым ... но он выполняет очень важный момент: простота кода. Я знаю, что ваш вопрос может быть упрощением, чтобы просто разоблачить проблему, с которой вы сталкиваетесь, и если это так, то предоставленное решение может не помочь ... но вот оно, если это так: просто используйте проставку , Я могу придумать два простых способа сделать это. Один быстрее, но грязнее, а другой немного элегантнее, но требует еще нескольких строк кода:

enter image description here

# 1 Быстрый и Грязный: Использовать невидимое изображение галочки в качестве разделителя.

struct ContentView: View {
    @State private var text = ""

    var body: some View {
        EasyModeQuestion(field: self.$text)
            .frame(width: 400)
    }
}

struct EasyModeQuestion: View {

    @Binding var field : String

    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Image(systemName: "checkmark.square")

                Text("What is the blah blah blah?")

            }

            HStack {
                Image(systemName: "checkmark.square").opacity(0.0)

                TextField("", text: $field)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }

        }

    }
}

# 2 Nicer spacer?: Использовать ширину изображения для представления Color.clear.

struct ContentView: View {
    @State private var text = ""

    var body: some View {
        EasyModeQuestion(field: self.$text)
            .frame(width: 400)
    }
}

struct EasyModeQuestion: View {

    @Binding var field : String

    var body: some View {
        let img = UIImage(systemName: "checkmark.square")!

        return VStack(alignment: .leading) {
            HStack {
                Image(uiImage: img)

                Text("What is the blah blah blah?")

            }

            HStack {
                Color.clear.frame(width: img.size.width, height: 0)

                TextField("", text: $field)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }
        }
    }
}
0 голосов
/ 01 апреля 2020

SwiftUI выполняет компоновку иначе, чем UIKit AutoLayout

  1. Делает подпредставления, рассчитывают все intrinsi c размеры контента
  2. Выполняют макет для них

Так выравнивание применяется после того, как TextField вычисляет его размер, но TextField не имеет собственного размера, поэтому он захватывает всю возможную ширину, предложенную родительским представлением, ie. экран шириной. Затем, когда выравнивание применяется, все становится беспорядочным.

Таким образом, вы как-то ограничиваете размер TextField значением, которое вы хотите (кстати, какой - вы не указали?)

Случай 1: жесткий код - как вы сделали с .frame(width: 200)

Случай 2: фиксированный размер для заполнителя

TextFieldRoundedRect(placeholder: "your answer is here", text: $field)
    .alignmentGuide(.questionAlignment, computeValue: { d in d[.leading] })
    .background(Color.green)
    .fixedSize()

demo

Случай 3 : рассчитать динамически (GeometryReader, AnchorPreference и т. д. c.) до необходимой вам точки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...