UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to generate points in text? 🙁

Forums > SwiftUI

Good evening everyone.

Guys, tell me how to generate points in the text?

Example:

proteins/100 g................................. 1.3 g

carbohydrates/100 g....................... 1.3g

2      

You would need to get the length of the longest string first and then fill the blanks with dots. I don't know if there is a library or a function in Foundation which can do this. But there is a bit caveat! You need a font where every symbol has the same width aka monospaced. Otherwise, the result would depend on the used font. F.e. your first line has 54 columns and your second line only has 48 columns. I hope you understand what I mean. And with a monospaced font your lines don't line up...

2      

Steven gets right to the point....

Guys, tell me how to generate points in the text?

Playgrounds

Try this code in Playgrounds. As @Hatsushira points out, to get a reasonable alignment, you need to use a monospaced font, such as Courier.

struct StudentView: View {
    var students = ["Harry Potter", "Dobby", "Hermione Granger", "Luna Lovegood", "Ginny Weasley", "Ron Weasley", "Albus Dumbledore"]
    var pageNumber: Int { Int.random(in: 35...42) }
    var randomStudent: String {
        // Note how the padding character fills each string out to 25 places before appending the page number
        students.randomElement()!.padding(toLength: 25, withPad: ".", startingAt: 0) + String(pageNumber)
    }
    var body: some View {
        VStack(alignment: .leading) {
            ForEach(0..<9) { _ in
                // Try replacing the custom Courier font with Helvetica
                Text(randomStudent).font(.custom("Courier", size: 18))
                // Text(randomStudent).font(.custom("Helvetica", size: 18))    
            }
        }
    }
}

Keep coding!

Please let us know how you solved your problem!

2      

I am very grateful for your advice and help!!!

Got perverted and did this....

import SwiftUI

struct TestWidth: View {

    let descriptionModel = [
        CharacteristicModel(name: "Производство", title: "Россия, Краснодарский край"),
        CharacteristicModel(name: "Энергетическая ценность, ккал/100гр", title: "25 ккал, 105 кДж"),
        CharacteristicModel(name: "Жиры/100 г", title: "0,1 г"),
        CharacteristicModel(name: "Белки/100 г", title: "1,3 г"),
        CharacteristicModel(name: "Углеводы/100 г", title: "1,3 г")
    ]

    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            ForEach(descriptionModel, id: \.self) { element in
                TestWidthrInfo(title: element.name, text: element.title)
            }
        }
    }
}

private struct TestWidthrInfo: View {

    let title: String
    let text: String

    var body: some View {
        ZStack(alignment: Alignment(horizontal: .center, vertical: .firstTextBaseline)) {
            Lines()
                .stroke(style: StrokeStyle(lineWidth: 1, dash: [2]))
                .frame(height: 1)
            HStack(alignment: .top, spacing: 0) {
                TitledForLabel(title: title, alignment: .leading)
                Spacer()
                TitledForLabel(title: text, alignment: .trailing)
            }
        }
    }
}

private struct TitledForLabel: View {

    let title: String
    var alignment: Alignment
    private var textAlignment: TextAlignment {
        calculateAlignment()
    }

    private let width: CGFloat = UIScreen.main.bounds.width
    private let currentFont: UIFont = .systemFont(ofSize: 14, weight: .semibold)
    @State private var widthTitle: CGFloat = 0

    @State var totalWidth: CGFloat = 0

    var body: some View {
        ZStack(alignment: alignment) {
            Rectangle()
                .fill(Color.white)
                .frame(width: totalWidth, height: 10)
            Text(title)
                .fontWeight(.semibold)
                .font(.system(size: 14))
                .fixedSize(horizontal: false, vertical: true)
                .clipped()
                .multilineTextAlignment(textAlignment)
                .foregroundColor(.black)
                .frame(maxWidth: width / 2, alignment: alignment)
        }
        .onAppear {
            calculateWidth()
        }
    }

     func calculateWidth() {
        var current: CGFloat = 5
        let components = title.components(separatedBy: .whitespacesAndNewlines)
        for component in components {
            current += component.widthOfText(currentFont: currentFont)
            if current <= width / 2 {
                totalWidth = current
            }
        }
    }

    private func calculateAlignment() -> TextAlignment {
        var directional: TextAlignment = .center
        switch alignment {
        case .leading:
            directional = .leading
        case .trailing:
            directional = .trailing
        default:
            break
        }
        return directional
    }
}

struct Lines: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: 2, y: 0))
        path.addLine(to: CGPoint(x: rect.width, y: 0))
        return path
    }
}

#if DEBUG
struct TestWidth_Previews: PreviewProvider {
    static var previews: some View {
        TestWidth()
    }
}
#endif

2      

Hi Steven,

Maybe your case is more complicated than I imagine, but if you just need to display lables why to make all those calculations. Maybe simple approach will be much easier. About the cost? Well I do not think that all those dots, if you make them even 1000 to make sure ipad or other device looks good as well, will take so "much" memory.

struct ContentView: View {
    var body: some View {
        VStack {
            CharacteristicModel(name: "Proteins/100 ", title: "1.3 g")
            CharacteristicModel(name: "Chabohydrates/100 ", title: "1.3 g")
            CharacteristicModel(name: "Whatever you want to palce it / 100 g", title: "1.3 g")
        }
        .padding()
    }
}

struct CharacteristicModel: View {
    var name: String
    var title: String

    var dottedLine: String {
        var line = ""
        for i in 0...1000 {
            line.append(".")
        }
        return line
    }

    var body: some View {
        HStack {
            Text(name)
            Text(dottedLine)
                .layoutPriority(-1) // to make sure it will be truncated first when no space available
            Text(title)
        }
        .lineLimit(1)
    }
}

2      

Hi, Yuri! Thanks for the help. But I have already implemented it in this clumsy way. Since I have text in two lines. I simply compared the width of the text block to the length of the words and if a certain number of words is greater than the width. Then I take the previous value and substitute it in the mask 🧐 Clumsy, but it works!

Although, following your example, you can calculate the length and enter points, I didn’t think of that.

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.