Search code examples
swiftswiftuistring-interpolationsf-symbolsswiftui-text

Is there a way to use string interpolation with an SF Symbol that has a modifier on it?


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'


Solution

  • 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
        }
    }