You can accomplish this entirely with constraints, defined either in Interface Builder, or in code. The trick is to define constraints that prevent the label from moving out of bounds that have higher priority than the constraints that set the desired position.
In my test project I set up a view hierarchy entirely in a storyboard having a 1) view controller view 2) "container view" which defines the bounds 3) multi-line UILabel. There are 6 constraints acting on the label from its container:
4 'space to' constraints (leading, trailing, top, bottom) prevent the label from ever being positioned outside the bounds of its parent container. The priority on these is set to the default '1000' value in Interface Builder. The relation for these constraints is '>=', and the constant value is '0'.
2 'space to' constraints (leading, top) drive the label's actual position. The priority on these is set lower; I chose '500'. These constraints have outlets in the view controller so they can be adjusted in code. The relation for these constraints is '=', and the initial value is whatever you want to position the label.
The label itself has a width constraint to force it to display with multiple lines.
Here's what this looks like in IB:
The selected constraint has a lower priority and is used to drive the x position of the label. This constraint is tied to an ivar in the view controller so it can be adjusted at runtime.
The selected constraint has a higher priority and is used to corral the label within its parent view.
And here is the code in the view controller:
@interface TSViewController ()
@end
@implementation TSViewController
{
IBOutlet NSLayoutConstraint* _xLayoutConstraint;
IBOutlet NSLayoutConstraint* _yLayoutConstraint;
}
- (IBAction) pan: (UIGestureRecognizer*) pgr
{
CGPoint p = [pgr locationInView: self.view];
p.x -= pgr.view.frame.size.width / 2.0;
p.y -= pgr.view.frame.size.height / 2.0;
_xLayoutConstraint.constant = p.x;
_yLayoutConstraint.constant = p.y;
}
@end
The UIPanGestureRecognizer
is associated with the UILabel and has its callback set to the pan: method in the view controller.