lexrus / LTMorphingLabel

[EXPERIMENTAL] Graceful morphing effects for UILabel written in Swift.
MIT License
8.07k stars 782 forks source link

Prevents a crash when embedding label in a designable view #19

Closed afdreher closed 9 years ago

afdreher commented 9 years ago

I really like the label, but it keeps crashing the IBDesignables agent when it is programmatically embed it inside an IBDesignable view. The problem is that the IBDesignables agent can't instantiate a display link object to animate the change from empty to whatever other text one provides. Here, I've made 3 changes to get the label to work:

1) Added the designable tag to the class 2) Made the animation conditional on the target not being the IB agent. This prevents the above issue with the display link. 3) Forced the _originText to the new value for the IB agent. This is necessary to get the label to draw the characters correctly. If the value isn't set, then without the animation, the label will be blank (it's previous value).

I think this is the minimal change required to get the designables behavior, but it might not be the cleanest.

lexrus commented 9 years ago

Great. Thanks a lot.

lexrus commented 9 years ago

@afdreher screen shot 2015-01-08 at 3 35 13 pm

IB failed to run the code after adding auto layout demo. I had to do a workaround in the next commit.

afdreher commented 9 years ago

I'm really sorry that patch broke on your end. It's a pretty curious issue, but I tracked it down after a bit of experimentation: it seems that it manifests itself after you build a test target. Since I never returned to the storyboard after running the tests, I never saw the issue on my end. I'll chalk this one up to one of the many, many frustrating curiosities of Xcode.

Sadly, working with designables has been a real pain point and none of the newer builds seem to make that any better. I pulled a fresh copy of the project from Github, but now I'm (1) not seeing anything and (2) getting either a warning in Xcode 6.2 or an error in Xcode 6.1.1.

blank with error

Again, sorry this made such a mess. The problem with conditionally executing the text selection function is that the drawing code requires the character positions to be computed and the layout to execute. As far as I can tell, the best way to get IB to behave with the designable is to somehow skip the animation, but otherwise perform everything else.

I'm going to try to poke around the code a bit more to see if I can get rid of these gremlins.

afdreher commented 9 years ago

With a little more digging, I think it might be related to this issue: http://stackoverflow.com/questions/26030951/ibdesignable-errors-when-adding-to-tests-target

It seems that it's more of an access control / permissions issue. I'll see if I can resolve it.

lexrus commented 9 years ago

@afdreher I've just read this page few hours ago...But I have no idea about what's going on.

afdreher commented 9 years ago

@lexrus Yeah, I need to have a bit better look myself. I think some of this advice might be from when designable had to live in a separate framework, but nonetheless I'll try modifying the visibility from module to public to see if it makes any difference.

I'm really staring to wonder about the true utility of designables. They're cool and helpful when they work, but the more I try to debug these things the less I'm inclined to recommend them. It's just too painful to try to get everything to behave. Sadly, the best way I've found to be debug them, which isn't ideal is: http://justabeech.com/2014/08/05/debugging-xcode-live-rendering/.

I think my plan of attack is to drop the designables code altogether and slowly reassemble the pieces. Just as a hint for me: is there any explicit "finish animation" code that I should be calling instead of trying to manually override the text setting?

lexrus commented 9 years ago

Again, read the 2nd link few hours ago...

Anyway, we may stop the animation here:

        if _originText != text {
#if !TARGET_INTERFACE_BUILDER
            displayLink.paused = false
#endif

And, the following override is senseless in IB.

override public func drawTextInRect(rect: CGRect)
lexrus commented 9 years ago

@afdreher I'm sorry I gonna sleep. Have a nice day. Zzzzz.....

afdreher commented 9 years ago

@lexrus Have a good night (morning here, so I'll continue playing...).

afdreher commented 9 years ago

I pulled a clean version of my fork to try to play with it. What I found is that you need this change to have the designable render when it's embedded inside another designable / view.

#if TARGET_INTERFACE_BUILDER
        _originText = newValue ?? ""
#else
        _originText = text ?? ""
#endif

Yes, simply skipping the displayLink appears to be safe and probably all that is necessary.

        if _originText != text {
#if !TARGET_INTERFACE_BUILDER
            displayLink.paused = false
#endif

Neither of these changes whether or not I see the XCtest issue though. I think I'm going to try to make a super simple project and see if I can elicit this bad behavior in case I can file a radar on it.