Michał Ziobro
Michał Ziobro

Reputation: 11752

How do style Text view with different font and colour on string subranges?

I want to style my Text view such that string it displays has different colours for each of 2 words. I know I can just use 2 x Text with different style. But then I should keep this string words in separate state variables. But I consider whether it is possible to define different .font().foregroundColor() for different parts of string.

There are also cases when there is longer lead and we want only first sentence or first word to be bolded, and remaining text to have regular font.

Any idea how to achieve this in SwiftUI?

Upvotes: 29

Views: 27110

Answers (5)

bjnortier
bjnortier

Reputation: 2048

You can use Text() + Text() with .reduce(), starting off with an empty Text("") variable. I you want to alternate a Text on with green and blue for subsequent words:

import SwiftUI

struct StackOverflowText: View {
    let text: String

    var body: some View {
        let segments = text.split(separator: " ")
        segments.enumerated().reduce(Text("")) { acc, pair in
            acc + Text(pair.1 + " ")
                .foregroundStyle(pair.0 % 2 == 0 ? .green : .blue)
        }
    }
}

#Preview {
    StackOverflowText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
        .padding()
}

Which gives you this:

enter image description here

Upvotes: 1

Petro Hupalo
Petro Hupalo

Reputation: 113

Simple solution with SwiftUI:

struct ExampleView: View {
    var body: some View {
        Text("\(Text("Text with blue color").coloredText(.blue)) \(Text("Text with yellow color").coloredText(.yellow))")
            .padding(20)
    }
}

extension Text {
    func coloredText(_ color: Color) -> Text {
        self.foregroundColor(color)
    }
}

Upvotes: 9

yasin89
yasin89

Reputation: 173

If you want to specific font and color you can use like below :

 VStack{
            Text("Your First Text")
                .font(.custom("Poppins-Medium", size: 12))
                .foregroundColor(Color(uiColor: UIColor(red: 0.631, green: 0.678, blue: 0.769, alpha: 1)))
                
            Text("Your Second Text")
                .font(.custom("Helvetica-Medium", size: 20))
                .foregroundColor(Color(uiColor: UIColor(red: 0.631, green: 0.678, blue: 0.769, alpha: 1)))
        }

Upvotes: 0

Kudos
Kudos

Reputation: 1490

If you want something like:

enter image description here

Instead of using an HStack, use the addition operator like this:

Group {
        Text("I am an ")
            .foregroundColor(Color.red) +
        Text("iOS ")
            .foregroundColor(Color.black)
            .fontWeight(.heavy) +
        Text("Developer:")
            .foregroundColor(Color.blue)
            .fontWeight(.bold)
    }

Upvotes: 27

Lineous
Lineous

Reputation: 1782

You can alternate formatting on a String with a ForEach loop wrapped in an HStack, although that comes with its own issues. The following code will let you conditionally apply formatting based on the word's location in the string:

struct FancyTextView: View {

    let label: String

    var splicedLabel: [String] {
        return label.split(separator: " ").map(String.init)
    }

    var body: some View {
    
        HStack {
            ForEach(0..<splicedLabel.count, id: \.self) { index in
                Text(self.splicedLabel[index])
                    .fontWeight(index.isMultiple(of: 2) ? .bold : .regular)
                    .foregroundColor(index.isMultiple(of: 2) ? .blue : .red)
            }
        }
    
    }
}

String formatting example

Unfortunately, this simple solution doesn't work with longer strings:

Poor formatting for longer strings

I thought recursion might be useful, but I couldn't think of a way to use recursion with SwiftUI's Text() + Text() syntax.

Still, you could modify the above code to only format the first word with a larger font and then you could use the Text() + Text() syntax:

First word formatting

This also removes the unpleasant formatting of the HStack and ForEach combination with text. Just change the body to the following:

Text(splicedLabel[0] + " ")
        .font(.title)
        .fontWeight(.bold)
    + Text(splicedLabel[1])

If you use this example, make sure you change the maxSplits parameter in .split() to 1 and check to make sure that there are at least two strings in the resulting array.

Upvotes: 37

Related Questions