#swift #swiftui #xcode11 #swiftui-text #swiftui-button
#swiftui
Вопрос:
У меня очень длинный текст, и я хочу показать всего 3 строки с кнопкой «больше«, как на картинке, а также с кнопкой «меньше», когда текст расширяется. Есть идеи, как это сделать с помощью SwiftUI?
var body: some View{
VStack{
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
}
}
Комментарии:
1. Смотрите обновление, пожалуйста
2. Рассматривали ли вы возможность использования модификатора
Button
в.overlay
представлении, который применяется к вашемуText
представлению?3. Не могли бы вы использовать код, чтобы показать мне?
Ответ №1:
Этот ответ немного взламывает, потому что он не усекает фактическую строку и не применяет суффикс «…», что, по моему скромному мнению, было бы лучшим инженерным решением. Для этого программисту потребуется определить длину строки, которая умещается в трех строках, удалить последние два слова (чтобы разрешить кнопку «Больше / меньше») и применить суффикс «…».
Это решение ограничивает количество отображаемых строк и буквально закрывает конец третьей строки белым фоном и кнопкой. Но это может подойти для вашего случая…
@State private var isExpanded: Bool = false
var body: some View{
VStack{
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
.lineLimit(isExpanded ? nil : 3)
.overlay(
GeometryReader { proxy in
Button(action: {
isExpanded.toggle()
}) {
Text(isExpanded ? "Less" : "More")
.font(.caption).bold()
.padding(.leading, 8.0)
.padding(.top, 4.0)
.background(Color.white)
}
.frame(width: proxy.size.width, height: proxy.size.height, alignment: .bottomTrailing)
}
)
}
}
Вы можете узнать, как это сделать, следуя инструкциям Apple «Знакомство с SwiftUI«. В частности, в руководстве «Создание приложения для macOS», «Раздел 9 Создание подробного представления«.
Комментарии:
1. Спасибо за быстрый ответ. На самом деле это сработало, но если я сделаю текст немного длиннее, он не отобразит целостный текст
2. Когда я добавляю FixedSize к тексту («»), весь текст отображается, но не прокручивается. Кажется, что что-то блокирует прокрутку вверх или вниз
3. В зависимости от используемого вами программного обеспечения могут возникнуть проблемы при попытке использовать
.fixedSize
модификатор — он работает не так, как ожидалось. Сообщите Apple об этом, отправив отзыв.
Ответ №2:
Я также пытался достичь тех же результатов, что и вы. Также потратил часы на поиск решения, когда оно все время было передо мной …!
Решение состоит в том, чтобы использовать ZStack вместе с ScrollView и GeometaryReader одновременно…
struct CollapsableTextView: View {
let lineLimit: Int
@State private var expanded: Bool = false
@State private var showViewButton: Bool = false
private var text: String
init(_ text: String, lineLimit: Int) {
self.text = text
self.lineLimit = lineLimit
}
private var moreLessText: String {
if showViewButton {
return expanded ? "View Less" : "View More"
} else {
return ""
}
}
var body: some View {
VStack(alignment: .leading) {
ZStack {
Text(text)
.font(.body)
.lineLimit(expanded ? nil : lineLimit)
ScrollView(.vertical) {
Text(text)
.font(.body)
.background(
GeometryReader { proxy in
Color.clear
.onAppear {
showViewButton = proxy.size.height > CGFloat(22 * lineLimit)
}
.onChange(of: text) { _ in
showViewButton = proxy.size.height > CGFloat(22 * lineLimit)
}
}
)
}
.opacity(0.0)
.disabled(true)
.frame(height: 0.0)
}
Button(action: {
withAnimation {
expanded.toggle()
}
}, label: {
Text(moreLessText)
.font(.body)
.foregroundColor(.orange)
})
.opacity(showViewButton ? 1.0 : 0.0)
.disabled(!showViewButton)
.frame(height: showViewButton ? nil : 0.0)
}
}
}
struct CollapsableTextView_Previews: PreviewProvider {
static var previews: some View {
CollapsableTextView("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", lineLimit: 3)
}
}
Наиболее важная часть этого кода CGFloat(22 * lineLimit)
здесь 22 — это высота отдельной строки с указанным используемым шрифтом. Возможно, вам придется изменить высоту (в данном случае 22) в зависимости от вашего шрифта…
В остальном все довольно просто. Я надеюсь, что это может помочь …!
Ответ №3:
Вы можете прочитать эту статью Medium
struct ExpandableText: View {
@State private var expanded: Bool = false
@State private var truncated: Bool = false
@State private var shrinkText: String
private var text: String
let font: UIFont
let lineLimit: Int
init(_ text: String, lineLimit: Int, font: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)) {
self.text = text
_shrinkText = State(wrappedValue: text)
self.lineLimit = lineLimit
self.font = font
}
var body: some View {
ZStack(alignment: .bottomLeading) {
Group {
Text(self.expanded ? text : shrinkText) Text(moreLessText)
.bold()
.foregroundColor(.black)
}
.animation(.easeInOut(duration: 1.0), value: false)
.lineLimit(expanded ? nil : lineLimit)
.background(
// Render the limited text and measure its size
Text(text)
.lineLimit(lineLimit)
.background(GeometryReader { visibleTextGeometry in
Color.clear.onAppear() {
let size = CGSize(width: visibleTextGeometry.size.width, height: .greatestFiniteMagnitude)
let attributes:[NSAttributedString.Key:Any] = [NSAttributedString.Key.font: font]
///Binary search until mid == low amp;amp; mid == high
var low = 0
var heigh = shrinkText.count
var mid = heigh ///start from top so that if text contain we does not need to loop
///
while ((heigh - low) > 1) {
let attributedText = NSAttributedString(string: shrinkText moreLessText, attributes: attributes)
let boundingRect = attributedText.boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)
if boundingRect.size.height > visibleTextGeometry.size.height {
truncated = true
heigh = mid
mid = (heigh low)/2
} else {
if mid == text.count {
break
} else {
low = mid
mid = (low heigh)/2
}
}
shrinkText = String(text.prefix(mid))
}
if truncated {
shrinkText = String(shrinkText.prefix(shrinkText.count - 2)) //-2 extra as highlighted text is bold
}
}
})
.hidden() // Hide the background
)
.font(Font(font)) ///set default font
///
if truncated {
Button(action: {
expanded.toggle()
}, label: {
HStack { //taking tap on only last line, As it is not possible to get 'see more' location
Spacer()
Text("")
}.opacity(0)
})
}
}
}
private var moreLessText: String {
if !truncated {
return ""
} else {
return self.expanded ? " read less" : " ... read more"
}
}
}
И используйте этот расширяемый текст в вашем представлении, как показано ниже
ExpandableText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut laborum", lineLimit: 6)