SwiftUI : [Unknown process name] CGAffineTransformInvert: singular matrix ERROR

Viewed 2740

Expectation: Green dot loading animation to occur when tapped on heart icon

Outcome: Nothing happens, feels like it freezes since the heart animation also doesn't work

Error: [Unknown process name] CGAffineTransformInvert: singular matrix.

Screenshot of the app(not working state)

Code:

struct ContentView: View {
    @State private var count=0
     @State var isPressed = false
    @State var pressed = false
    @State var isLoading = false
    var body: some View {
       
        VStack {
            ZStack{
                Circle()
                    .fill( isPressed ? Color(.systemGray4) : .red )
                    .overlay(Image(systemName:"heart.fill")
                        .foregroundColor(isPressed ? .red :.white)
                        .font(.system(size:100))
                        
                        .scaleEffect( pressed ? 1.5 : 1.0)
                )
                .frame(width: 300, height: 300)
                
                
            }
            
            .onTapGesture{
                self.isLoading.toggle()
                withAnimation(.spring(response: 1,dampingFraction: 0.4, blendDuration :0.9)){
                self.isPressed.toggle()
                    self.pressed.toggle()
                    self.isLoading.toggle()
                }
                
            }
        
    
            HStack{
                ForEach(0...4, id: \.self){index in
                    Circle()
                        .frame(width:10,height:10)
                        .foregroundColor(.green)
                        .scaleEffect(self.isLoading ? 1:0)
                        .animation(Animation.linear(duration:0.6).repeatForever().delay(0.2*Double(index)))
                }
            }

        .padding()

            
    }
    
    }
    
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Note: Loading animation works when I don't toggle the isLoading state variable inside onTapGesture() block and directly set it to true when the view appears inside onAppear() block

2 Answers

REASON :

The problem is basically with CGAffinTransform

When you use the modifier ScaleEffect to your view, in background the compiler performs some short of Transforms on the scale Value that you have provided.

Since you have provided value 0 (zero) for .scaleEffect(), its taking the size as zero and treating it as a singular Matrix ( its a type of matrix whose Determinant is Zero ). and its also considered a mathematical violation to perform operations on matrices with a determinant of zero.

SOLUTION :

Make sure if you are using .scaleEffect() , don't scale the View to exact 0. You can use the smallest possible value i.e 0.1 or 0.01

The problem is in 0 scaleEffect.

Here is fixed code. Tested with Xcode 11.4 / iOS 13.4. Also corrected animation for the case of isLoading = false

demo

struct ContentView: View {
    @State private var count=0
    @State var isPressed = false
    @State var pressed = false
    @State var isLoading = false
    var body: some View {

        VStack {
            ZStack{
                Circle()
                    .fill( isPressed ? Color(.systemGray4) : .red )
                    .overlay(Image(systemName:"heart.fill")
                        .foregroundColor(isPressed ? .red :.white)
                        .font(.system(size:100))

                        .scaleEffect( pressed ? 1.5 : 1.0)
                )
                .frame(width: 300, height: 300)
            }
            .onTapGesture{
                self.isLoading.toggle()
                withAnimation(.spring(response: 1,dampingFraction: 0.4, blendDuration :0.9)){
                    self.isPressed.toggle()
                    self.pressed.toggle()
                }
            }

            HStack{
                ForEach(0...4, id: \.self){index in
                    Circle()
                        .frame(width:10,height:10)
                        .foregroundColor(.green)
                        .scaleEffect(self.isLoading ? 1:0.01)
                        .animation(self.isLoading ? Animation.linear(duration:0.6).repeatForever().delay(0.2*Double(index)) :
                            .default
                        )
                }
            }
            .padding()
        }
    }
}
Related