I want to use string interpolation on an SF Symbol that has a rotationEffect(_:anchor:)
modifier applied to it. Is it possible to do this?
Without the modifier this type of string interpolation works fine (in Swift 5.0):
struct ContentView: View {
var body: some View {
Text("Some text before \(Image(systemName: "waveform.circle")) plus some text after.")
}
}
But applying the modifier like this:
struct ContentView: View {
var body: some View {
Text("Some text before \(Image(systemName: "waveform.circle").rotationEffect(.radians(.pi * 0.5))) plus some text after.")
}
}
doesn't compile and gives this error:
Instance method 'appendInterpolation' requires that 'some View' conform to '_FormatSpecifiable'
CodePudding user response:
The Text interpolation expect an Image
. When the .rotationEffect...
is applied it becomes a View
, and this is not valid.
So an alternative is to rotate the SF before it is used in Image
.
This is what I ended up trying, using the code from one of the answers at: Rotating UIImage in Swift
to rotate a UIImage
and using that in the Image
.
It is a bit convoluted, and you will probably have to adjust the anchor/position. Perhaps someone will come up with a better solution. Until then it seems to works for me.
struct ContentView: View {
var body: some View {
Text("Some text before \(img) plus some text after.")
}
var img: Image {
if let uimg = UIImage(systemName: "waveform.circle"),
let rotImage = uimg.rotate(radians: .pi/4) {
return Image(uiImage: rotImage)
} else {
return Image(systemName: "waveform.circle")
}
}
}
// from: https://stackoverflow.com/questions/27092354/rotating-uiimage-in-swift
extension UIImage {
func rotate(radians: Float) -> UIImage? {
var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
// Trim off the extremely small float value to prevent core graphics from rounding it up
newSize.width = floor(newSize.width)
newSize.height = floor(newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, self.scale)
let context = UIGraphicsGetCurrentContext()!
// Move origin to middle
context.translateBy(x: newSize.width/2, y: newSize.height/2)
// Rotate around middle
context.rotate(by: CGFloat(radians))
// Draw the image at its center
self.draw(in: CGRect(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height: self.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
CodePudding user response:
As pointed out here by workingdog_support_Ukraine, string interpolation will work with Image
but modifiers will change the type they are applied to. So we need to rotate the image without erasing the Image
type.
For simple orientation
rotations we can create our rotated Image
type like this:
extension Image {
init(systemName: String, orientation: UIImage.Orientation) {
guard
let uiImage = UIImage(systemName: systemName),
let cgImage = uiImage.cgImage
else {
self.init(systemName: systemName)
return
}
self.init(uiImage: UIImage(cgImage: cgImage, scale: uiImage.scale, orientation: orientation))
}
}
We then use a rotated image (in this case, rotated 90 degrees clockwise) in our string interpolation, as follows:
struct ContentView: View {
var body: some View {
Text("Some text before \(Image(systemName: "waveform.circle", orientation: .right)) plus some text after.")
}
}
This aligns the rotated image on the text baseline.