How to make text stroke in SwiftUI?
I find another trick to create stroke, but it only works if your desired stroke width is not more than 1
Text("Hello World")
.shadow(color: .black, radius: 1)
I used shadow
, but make sure the radius is just 1, to get the same efffect
I don't think there's a way for doing that "out of the box".
So far (beta 5) we can apply strokes to Shapes
only.
For example:
struct SomeView: View {
var body: some View {
Circle().stroke(Color.red)
}
}
But again that isn’t available for Text
.
UIViewRepresentable
Another approach would be to use the good ol' UIKit
\ NSAttributedString
with SwiftUI via UIViewRepresentable
.
Like so:
import SwiftUI
import UIKit
struct SomeView: View {
var body: some View {
StrokeTextLabel()
}
}
struct StrokeTextLabel: UIViewRepresentable {
func makeUIView(context: Context) -> UILabel {
let attributedStringParagraphStyle = NSMutableParagraphStyle()
attributedStringParagraphStyle.alignment = NSTextAlignment.center
let attributedString = NSAttributedString(
string: "Classic",
attributes:[
NSAttributedString.Key.paragraphStyle: attributedStringParagraphStyle,
NSAttributedString.Key.strokeWidth: 3.0,
NSAttributedString.Key.foregroundColor: UIColor.black,
NSAttributedString.Key.strokeColor: UIColor.black,
NSAttributedString.Key.font: UIFont(name:"Helvetica", size:30.0)!
]
)
let strokeLabel = UILabel(frame: CGRect.zero)
strokeLabel.attributedText = attributedString
strokeLabel.backgroundColor = UIColor.clear
strokeLabel.sizeToFit()
strokeLabel.center = CGPoint.init(x: 0.0, y: 0.0)
return strokeLabel
}
func updateUIView(_ uiView: UILabel, context: Context) {}
}
#if DEBUG
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
#endif
Result
Of course you have to tweak the attributes (size, font, color, etc) of the NSAttributedString
to generate the desired output. For that I would recommend the Visual Attributed String macOS app.
Here is a 100% SwiftUI solution. Not perfect, but it works and it gives you full SwiftUI control of the resulting view.
import SwiftUI
struct SomeView: View {
var body: some View {
StrokeText(text: "Sample Text", width: 0.5, color: .red)
.foregroundColor(.black)
.font(.system(size: 12, weight: .bold))
}
}
struct StrokeText: View {
let text: String
let width: CGFloat
let color: Color
var body: some View {
ZStack{
ZStack{
Text(text).offset(x: width, y: width)
Text(text).offset(x: -width, y: -width)
Text(text).offset(x: -width, y: width)
Text(text).offset(x: width, y: -width)
}
.foregroundColor(color)
Text(text)
}
}
}
I suggest using bold weight. It works better with reasonably sized fonts and stroke widths. For larger sizes, you may have to add Text offsets in more angles to cover the area.
You can do this with SwiftFX
import SwiftUI
import SwiftFX
struct ContentView: View {
var body: some View {
Text("Hello, World!")
.fxEdge()
}
}
Here's the Swift Package:
.package(url: "https://github.com/hexagons/SwiftFX.git", from: "0.1.0")
Setup instructions here.