0

I have created my own implementation of a UITabBar (just a UIView instance).

This UIView contains 3 TabBarItem instances. A TabBarItem is a UIView subclass, and each contains the following UI controls:

  • UIImageView
  • UILabel
  • TabBarBadge (custom UIView subclass)

I am laying out the view hierarchy in a storyboard using auto layout.

The badge for a tab bar item should be positioned so that the y-center of the badge is aligned with the top edge of the image view, and it should be aligned to the right side of the image view, with a 4 point overlap (so, left edge of badge aligned with right edge of image view, with a constant of -4).

This works fine when the app loads, whether in portrait or landscape mode. In either case, though, rotating the device ends up with the frame of the badge in the wrong place after rotation finishes.

Here's a printout from the console of the NSLayoutConstraint in question:

<NSLayoutConstraint H:[UIImageView]-(-4)-[TabBarBadge] (active)>

That's exactly what I would expect it to be.

Here's a printout from the console of the TabBarBadge in question (portrait mode; correct presentation):

<TabBarBadge frame = (80.6667 0; 36 20); text = '67'; autoresize = RM+BM>

Here's a printout from the console of the TabBarBadge in question (landscape mode; incorrect presentation):

<TabBarBadge frame = (80.6667 0; 36 20); text = '67'; autoresize = RM+BM>

So, you can see that the frame isn't changing/updating when the rotation occurs.

Here are a couple of screen shots:

Portrait, Correct Display

Portrait, Correct Display

Landscape, Incorrect Display

Landscape, Incorrect Display

Constraints (Printout of constraints above is from this view)

Constraints

So, what has me confused is that the rest of the TabBarItem is updating correctly. The UIImageView and UILabel are automatically updating correctly. Why is this one frame not updating correctly?

Community
  • 1
  • 1
mbm29414
  • 11,558
  • 6
  • 56
  • 87
  • @matt Did you see the last screenshot? It's from the view debugger. And the printout of the `NSLayoutConstraint` is the description of the highlighted constraint. – mbm29414 Sep 15 '17 at 20:11
  • @matt Yes. The last screenshot is from the view debugger. The printout of the `NSLayoutConstraint` is from the view debugger. They don't match. It shows a correctly-configured constraint, but the frame is not correct per the settings of the constraint. I'm not sure what other suggestions you're making. – mbm29414 Sep 15 '17 at 20:16
  • @matt I even changed it up and removed the `NSLayoutConstraint` I showed above, replacing it with a constraint that aligned the view to the center of its superview, offset to the right by a static amount (which would position it in the same place). Same problem occurred. View debugger still shows the correct settings for the badge. Could it be because this `UIView` subclass does its own `drawRect:` method? I can't think of anything else that's unusual about this view. – mbm29414 Sep 15 '17 at 20:23

1 Answers1

0

So, first I researched whether a custom drawRect: method might be the cause of my problem. That doesn't appear to be a factor.

Then, I started noticing some odd discrepancies between the view's frame at different times, and how only this particular one (there are actually 3 in the app) was having the problem. The other 2 currently have no values displayed.

Then, I remembered that I use a UIDynamicAnimator when the value is changed to "bounce" the view, in order to bring attention to the new value.

So, the problem is actually that I wasn't "releasing" (in the control sense, not the memory sense) the view from the UIDynamicAnimator when I was done with the animation. (I'm still not sure whether that's a bug or not. It seems to me that if the animation is paused and the constraints should update the view's frame, then perhaps that should occur.)

So, here's the actual code that was "buggy":

- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
    // {Other code}
}

And here's the code that fixed my issue:

- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
    // {Other code}

    // Code that fixed the issue:
    [self.animator removeAllBehaviors];
}

If I need to animate the view again, I just re-create the animator anyway, so this seems to be the best fix.

This appears to be in agreement with Matt's answer here. (I didn't know this was an issue with UIDynamicAnimator when I asked the question.)

mbm29414
  • 11,558
  • 6
  • 56
  • 87