SWIFTUI Marquee, когда текст не подходит - PullRequest
0 голосов
/ 21 марта 2020

У меня есть текст, но он не подходит. Я хочу использовать marquee, когда текст не помещается в рамку по умолчанию.

Text(self.viewModel.soundTrack.title)
    .font(.custom("Avenir Next Regular", size: 24))
    .multilineTextAlignment(.trailing)
    .lineLimit(1)
    .foregroundColor(.white)
    .fixedSize(horizontal: false, vertical: true)
    //.frame(width: 200.0, height: 30.0)

1 Ответ

2 голосов
/ 22 марта 2020

Попробуйте код ниже ....

В MarqueeText.swift

import SwiftUI

struct MarqueeText: View {

    @State private var leftMost = false

    @State private var w: CGFloat = 0

    @State private var previousText: String = ""

    @State private var contentViewWidth: CGFloat = 0

    @State private var animationDuration: Double = 5

    @Binding var text : String

    var body: some View {
        let baseAnimation = Animation.linear(duration: self.animationDuration)//Animation duration
        let repeated = baseAnimation.repeatForever(autoreverses: false)
        return VStack(alignment:.center, spacing: 0) {
            GeometryReader { geometry in//geometry.size.width will provide container/superView width
                Text(self.text).font(.system(size: 24)).lineLimit(1).foregroundColor(.clear).fixedSize(horizontal: true, vertical: false).background(TextGeometry()).onPreferenceChange(WidthPreferenceKey.self, perform: {
                    self.w = $0
                    print("textWidth:\(self.w)")
                    print("geometry:\(geometry.size.width)")
                    self.contentViewWidth = geometry.size.width
                    if self.text.count != self.previousText.count && self.contentViewWidth < self.w {
                        let duration = self.w/50
                        print("duration:\(duration)")
                        self.animationDuration = Double(duration)
                        self.leftMost = true
                    } else {
                        self.animationDuration = 0.0
                    }
                    self.previousText = self.text
                    }).fixedSize(horizontal: false, vertical: true)// This Text is temp, will not be displayed in UI. Used to identify the width of the text.
                if self.animationDuration > 0.0 {
                    Text(self.text).font(.system(size: 24)).lineLimit(nil).foregroundColor(.green).fixedSize(horizontal: true, vertical: false).background(TextGeometry()).onPreferenceChange(WidthPreferenceKey.self, perform: { _ in
                                    if self.text.count != self.previousText.count && self.contentViewWidth < self.w {

                                    } else {
                                        self.leftMost = false
                                    }
                                    self.previousText = self.text
                        }).modifier(self.makeSlidingEffect().ignoredByLayout()).animation(repeated, value: self.leftMost).clipped(antialiased: true).offset(y: -8)//Text with animation
                }
                else {
                    Text(self.text).font(.system(size: 24)).lineLimit(1).foregroundColor(.blue).fixedSize(horizontal: true, vertical: false).background(TextGeometry()).fixedSize(horizontal: false, vertical: true).frame(maxWidth: .infinity, alignment: .center).offset(y: -8)//Text without animation
                }
            }
            }.fixedSize(horizontal: false, vertical: true).layoutPriority(1).frame(maxHeight: 50, alignment: .center).clipped()

    }


    func makeSlidingEffect() -> some GeometryEffect {
      return SlidingEffect(
        xPosition: self.leftMost ? -self.w : self.w,
        yPosition: 0).ignoredByLayout()
    }
}

struct MarqueeText_Previews: PreviewProvider {
    @State static var myCoolText = "myCoolText"
    static var previews: some View {
        MarqueeText(text: $myCoolText)
    }
}

struct SlidingEffect: GeometryEffect {
    var xPosition: CGFloat = 0
    var yPosition: CGFloat = 0

  var animatableData: CGFloat {
    get { return xPosition }
    set { xPosition = newValue }
  }

  func effectValue(size: CGSize) -> ProjectionTransform {
    let pt = CGPoint(
      x: xPosition,
      y: yPosition)
    return ProjectionTransform(CGAffineTransform(translationX: pt.x, y: pt.y)).inverted()
  }
}

struct TextGeometry: View {
    var body: some View {
        GeometryReader { geometry in
            return Rectangle().fill(Color.clear).preference(key: WidthPreferenceKey.self, value: geometry.size.width)
        }
    }
}

struct WidthPreferenceKey: PreferenceKey {
    static var defaultValue = CGFloat(0)

    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }

    typealias Value = CGFloat
}

struct MagicStuff: ViewModifier {
    func body(content: Content) -> some View {
        Group {
            content.alignmentGuide(.underlineLeading) { d in
                return d[.leading]
            }
        }
    }
}

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

В вашей существующей структуре SwiftUI. (Пример кода ниже будет проверять 3 случая: 1. Пустая строка, 2. Короткая строка, которая не должна выделяться, 3. Длинная строка выделения)

@State var value = ""
@State var counter = 0

var body: some View {
    VStack {
        Spacer(minLength: 0)
        Text("Monday").background(Color.yellow)
        HStack {
            Spacer()
            VStack {
                Text("One").background(Color.blue)
            }
            VStack {
            MarqueeText(text: $value).background(Color.red).padding(.horizontal, 8).clipped()
            }
            VStack {
            Text("Two").background(Color.green)
            }
            Spacer()

        }
        Text("Tuesday").background(Color.gray)
        Spacer(minLength: 0)
        Button(action: {
            self.counter = self.counter + 1
            if (self.counter % 2 == 0) {
                self.value = "1Hello World! Hello World! Hello World! Hello World! Hello World!"
            } else {
                self.value = "1Hello World! Hello"
            }
        }) {
            Text("Button")
        }
        Spacer()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...