Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animating fillVar has wrong behaviour with Auto Layout #708

Open
qe0 opened this issue Jun 27, 2020 · 8 comments
Open

Animating fillVar has wrong behaviour with Auto Layout #708

qe0 opened this issue Jun 27, 2020 · 8 comments

Comments

@qe0
Copy link
Contributor

qe0 commented Jun 27, 2020

Recently I faced with this issue.

public func animate<T>(from:,to:during:,delay:) where T:Macaw.Fill animates both size and color when should animates only color (fillVar).
And size is animating to original svg size and then gets back immediately (check gif below).

Here's bounds.

print(icon.bounds) // (0.0, 0.0, 100.0, 100.0)
print(icon.node.bounds!) (x: 0.000000, y: 0.000000, w: 24.000000, h: 24.000000)

As you see icon's bounds is actual for Auto Layout but node's bounds are the same as source svg has.

<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" d="..."/>
<path d="..."/>
</svg>

How I layout icon:

icon.contentMode = .scaleAspectFit
view.addSubview(icon)
icon.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    icon.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 80),
    icon.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 80),
    icon.heightAnchor.constraint(equalToConstant: 100),
    icon.widthAnchor.constraint(equalToConstant: 100)
])

How I update icon's color:

extension MacawView {
    func updateShapeColor(val: Int) {
        updateShape(node: self.node) { shape in
            let animation = shape.fillVar.animation(to: Color(val: val))
            animation.play()
        }
    }

    func updateShape(node: Node, closure: (Shape) -> Void) {
        if let group = node as? Group {
            group.contents.forEach { updateShape(node: $0, closure: closure) }
        } else if let shape = node as? Shape {
            closure(shape)
        }
    }
}

gif

My guesses are:

  • ContentLayout (ScalingContentLayout) causes wrong calculations for bounds of node (Group).
  • SceneUtils.shapeCopy() may original size instead of actual and cause unwanted interpolation of sizes too.
  • or dunno ¯_(ツ)_/¯
@qe0
Copy link
Contributor Author

qe0 commented Jun 27, 2020

#692 Oops.

@qe0 qe0 closed this as completed Jun 27, 2020
@qe0
Copy link
Contributor Author

qe0 commented Jun 28, 2020

According to #692 and #694

This pr actually fixes uneccessary scale animation but breaks layout at right start.
So I did example reproducing this bug.

When you change fillVar inside of viewDidLoad() without delay, it changes color with animation but saving original size from SVG code.
The next updates (after a while) go well, but with a quick blink to actual size which should be at the very beginning.

Code reproducing this bug:

import UIKit
import Macaw

class ViewController: UIViewController {
    let icon = MacawView(node: try! SVGParser.parse(resource: "icon"), frame: .zero)

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        icon.contentMode = .scaleAspectFit

        view.addSubview(icon)
        icon.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            icon.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120),
            icon.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40),
            icon.widthAnchor.constraint(equalToConstant: 120),
            icon.heightAnchor.constraint(equalToConstant: 120)
        ])

        // Wrong! Updates color with not actual size (120x120).
        icon.updateShapeColor(val: 0xff0000)

        // Good! Updates color with actual size (120x120),
        // but immediately changes icon size.
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.icon.updateShapeColor(val: 0x0000ff)
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.icon.updateShapeColor(val: 0x000000)
        }
    }
}

extension MacawView {
    func updateShapeColor(val: Int) {
        updateShape(node: self.node) { shape in
            shape.fillVar.animate(from: nil, to: Color(val: val), during: 1, delay: 0.0)
        }
    }

    func updateShape(node: Node, closure: (Shape) -> Void) {
        if let group = node as? Group {
            group.contents.forEach { updateShape(node: $0, closure: closure) }
        } else if let shape = node as? Shape {
            closure(shape)
        }
    }
}
Example SVG
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 5C2 3.34315 3.34315 2 5 2H15C16.6569 2 18 3.34315 18 5V6H19C20.6569 6 22 7.34315 22 9V19C22 20.6569 20.6569 22 19 22H9C7.34315 22 6 20.6569 6 19V18H5C3.34315 18 2 16.6569 2 15V5ZM5 4H15C15.5523 4 16 4.44772 16 5V15C16 15.5523 15.5523 16 15 16H5C4.44772 16 4 15.5523 4 15V5C4 4.44772 4.44772 4 5 4ZM8 18V19C8 19.5523 8.44772 20 9 20H19C19.5523 20 20 19.5523 20 19V9C20 8.44772 19.5523 8 19 8H18V15C18 16.6569 16.6569 18 15 18H8Z" fill="#6788F3"/>
<path opacity="0.5" d="M12.5 6C11.6716 6 11 6.67157 11 7.5V9C11 10.1046 10.1046 11 9 11H7.5C6.67157 11 6 11.6716 6 12.5C6 13.3284 6.67157 14 7.5 14H12.5C13.3284 14 14 13.3284 14 12.5V7.5C14 6.67157 13.3284 6 12.5 6Z" fill="#6788F3"/>
</svg>

1234

@qe0 qe0 reopened this Jun 28, 2020
@qe0
Copy link
Contributor Author

qe0 commented Jun 28, 2020

P.s. temporary workaround for this:
For initial setup: icon.updateShapeColor(val: 0x000000, animated: false)
For following updates: icon.updateShapeColor(val: 0x000000, animated: true)

    func updateShapeColor(val: Int, animated: Bool = true) {
        if animated {
            updateShape(node: self.node) { shape in
                shape.fillVar.animate(from: nil, to: Color(val: val), during: 1, delay: 0.0)
            }
        } else {
            updateShape(node: self.node) { shape in
                shape.fill = Color(val: val)
            }
        }
    }

@qe0
Copy link
Contributor Author

qe0 commented Sep 1, 2020

sigh
424

@qe0
Copy link
Contributor Author

qe0 commented Dec 1, 2020

once in a far, distant repository...
Untitled-1

@qe0
Copy link
Contributor Author

qe0 commented Jun 2, 2021

...little pepe faced a problem that was scarier than he thought...
Frame 1

@nikita-afonasov
Copy link
Member

Hi! I was tempted to wait until the issues anniversary, as it turns out this is here for almost a year. I have some news, but most of them are bad I think

  1. We didn't reply to you earlier, and for that I'm sorry.
  2. I spoke to the maintainers of the project, and they acknowledge the issue, but it seems it's something that can't be fixed that easily. Also, all of them have been busy in outstaffing roles, and those understandably take precedence over something we do for fun.
  3. We've been somewhat lackluster in supporting Macaw ever since Apple released SwiftUI and basically made our project obsolete (except for the SVG rendering part).

The bottom line is that we plan to fix this, but I can't promise when. At least we got some spicy memes out of it.

@qe0
Copy link
Contributor Author

qe0 commented Jun 2, 2021

@nikita-afonasov,
Thanks for the reply and glad to hear it. I hope some sort of fix will be released someday.
But for now, I will sink (unexpected Combine reference, wow) into the abyss of oblivion until something happens that will make me post pepes again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants