Совместите два текстовых представления SwiftUI в HStack с правильным выравниванием - PullRequest
0 голосов
/ 13 июня 2019

У меня есть простое представление списка, которое содержит две строки.

Каждая строка содержит два текстовых представления.Просмотр одного и просмотр двух.

Я хотел бы выровнять последний ярлык (просмотр двух) в каждой строке так, чтобы ярлыки имен были выровнены по началу и продолжали выравниваться независимо от размера шрифта.

Первая метка (View one) также должна быть выровнена по началу.

Я попытался установить минимальную ширину кадра для первой метки (View One), но она не работает.Также кажется невозможным установить минимальную ширину, а также заставить текстовое представление выравниваться по лиде в View One.

Есть идеи?Это довольно просто в UIKit.

List View

Ответы [ 5 ]

1 голос
/ 14 июня 2019

Вы можете просто получить два Text с, а затем Spacer в HStack.Spacer будет сдвигать ваши Text с влево, и все будет самонастраиваться, если Text s изменит размер из-за длины их содержимого:

HStack {
    Text("1.")
    Text("John Doe")
    Spacer()
}
.padding()

enter image description here

Text s технически выровнены по центру, но так как размеры автоматически изменяются и занимают столько же места, сколько текст внутри него (поскольку мы явно не устанавливали рамкуразмер), и Spacer сдвигаются влево, они выглядят выровненными по левому краю.Преимущество этого по сравнению с установкой фиксированной ширины заключается в том, что вам не нужно беспокоиться о том, что текст обрезается.

Кроме того, я добавил отступ к HStack, чтобы он выглядел лучше, но если вы хотитеОтрегулируйте, насколько близко Text s друг к другу, вы можете вручную установить отступы с любой из его сторон.(Вы можете даже установить отрицательное заполнение, чтобы подтолкнуть элементы ближе друг к другу, чем их естественный интервал).

Редактировать

Не понимал, что OP нужен второй Textбыть вертикально выровненным также.У меня есть способ сделать это, но он «хакерский» и не будет работать для больших размеров шрифта без дополнительной работы:

Это объекты данных:

class Person {
    var name: String
    var id: Int
    init(name: String, id: Int) {
        self.name = name
        self.id = id
    }
}

class People {
    var people: [Person]
    init(people: [Person]) {
        self.people = people
    }
    func maxIDDigits() -> Int {
        let maxPerson = people.max { (p1, p2) -> Bool in
            p1.id < p2.id
        }
        print(maxPerson!.id)
        let digits = log10(Float(maxPerson!.id)) + 1
        return Int(digits)
    }
    func minTextWidth(fontSize: Int) -> Length {
        print(maxIDDigits())
        print(maxIDDigits() * 30)
        return Length(maxIDDigits() * fontSize)
    }
}

ЭтоView:

var people = People(people: [Person(name: "John Doe", id: 1), Person(name: "Alexander Jones", id: 2000), Person(name: "Tom Lee", id: 45)])
var body: some View {   
    List {
        ForEach(people.people.identified(by: \.id)) { person in                
            HStack {
                Text("\(person.id).")
                    .frame(minWidth: self.people.minTextWidth(fontSize: 12), alignment: .leading)
                Text("\(person.name)")

            }
        }
    }
}

Чтобы он работал для шрифтов разных размеров, вам нужно будет получить размер шрифта и передать его в minTextWidth(fontSize:).

Опять же, я быХотелось бы подчеркнуть, что это «хакерство» и, вероятно, идет вразрез с принципами SwiftUI, но я не смог найти встроенный способ сделать макет, который вы просили (вероятно, потому что Text в разных строках не взаимодействуют друг с другом,поэтому они не могут знать, как оставаться вертикально выровненными друг с другом).

Редактировать 2 Приведенный выше код генерирует это:

Code result

1 голос
/ 14 июня 2019

Вы можете установить фиксированную ширину для вида Text. Это делает этот Text компонент с фиксированным размером.

image

HStack {
        Text(item.number)
            .multilineTextAlignment(.leading)
            .frame(width: 30)
        Text(item.name)
}

Недостаток этого решения заключается в том, что если у вас там будет более длинный текст, он будет обернут и оканчивается на «...», но в этом случае я думаю, что вы можете приблизительно оценить, какой ширины будет достаточно.

0 голосов
/ 28 июня 2019

Мне просто нужно было разобраться с этим. Решения, основанные на фиксированной ширине frame, не будут работать для динамического типа, поэтому я не мог их использовать. Чтобы обойти это, я поместил гибкий элемент (в данном случае левое число) в ZStack с заполнителем, содержащим самый широкий допустимый контент, а затем установил непрозрачность заполнителя в 0:

ZStack {
    Text("9999")
        .opacity(0)
        .accessibility(visibility: .hidden)
    Text(id)
}

Это довольно странно, но, по крайней мере, поддерживает динамический тип ?‍♂️

ZStack with placeholder

Полный пример ниже! ?

import SwiftUI

struct Person: Identifiable {
    var name: String
    var id: Int
}

struct IDBadge : View {
    var id: Int
    var body: some View {
        ZStack(alignment: .trailing) {
            Text("9999.") // The maximum width dummy value
                .font(.headline)
                .opacity(0)
                .accessibility(visibility: .hidden)
            Text(String(id) + ".")
                .font(.headline)
        }
    }
}

struct ContentView : View {
    var people: [Person]
    var body: some View {
        List(people) { person in
            HStack(alignment: .top) {
                IDBadge(id: person.id)
                Text(person.name)
                    .lineLimit(nil)
            }
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static let people = [Person(name: "John Doe", id: 1), Person(name: "Alexander Jones", id: 2000), Person(name: "Tom Lee", id: 45)]
    static var previews: some View {
        Group {
            ContentView(people: people)
                .previewLayout(.fixed(width: 320.0, height: 150.0))
            ContentView(people: people)
                .environment(\.sizeCategory, .accessibilityMedium)
                .previewLayout(.fixed(width: 320.0, height: 200.0))
        }
    }
}
#endif
0 голосов
/ 17 июня 2019

Я думаю, что правильный способ сделать это будет использовать HorizontalAlignment. Что-то вроде:

extension HorizontalAlignment {
    private enum LeadingName : AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> Length { d[.leading] }
    }
    static let leadingName = HorizontalAlignment(LeadingName.self)
}

List (people.identified(by: \.id)) {person in
    HStack {
        Text("\(person.id)")
        Text("\(person.name)").alignmentGuide(.leadingName) {d in d[.leading]}
    }
}

Но я не могу заставить его работать.

Я не могу найти никаких примеров этого со списком. Кажется, что List не поддерживает выравнивание (пока?)

Я могу заставить его работать с VStack и жестко закодированными значениями, такими как:

        VStack (alignment: .leadingName ) {
        HStack {
            Text("1.")
            Text("John Doe").alignmentGuide(.leadingName) {d in d[.leading]}
            Spacer()
        }
        HStack {
            Text("2000.")
            Text("Alexander Jones").alignmentGuide(.leadingName) {d in d[.leading]}
            Spacer()
        }
        HStack {
            Text("45.")
            Text("Tom Lee").alignmentGuide(.leadingName) {d in d[.leading]}
            Spacer()
        }
    }

Я надеюсь, что это будет исправлено в более поздней бета-версии ...

0 голосов
/ 14 июня 2019
HStack {
            HStack {
                Spacer()
                Text("5.")
            }
            .frame(width: 40)

            Text("Jon Smith")
        }

Но это будет работать только с фиксированной шириной..frame(minWidth: 40) заполнит весь просмотр, поскольку Space()

.multilineTextAlignment(.leading) не имеют никакого эффекта в моих тестах.

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