The problem is unique to the new iOS 15 UIButton API. There is no problem in my logic if the deployment target < iOS15.
Specifically,
I tried setting a constraint on the height of the button. It is ignored — the text wrap extends outside the constrained height.
I tried forcing the button to be very wide. That works (i.e., no wrap), but in this case, the button extends beyond the trailing edge of the image, which leads to undesirable effects.
I tried setting .numberOfLines to 1. In fact, I do so whenever changing title content. The button sets the value back to 0 and wraps the text.