动态/可拖动UIView的自动布局

问题描述:

我想在我的ViewController中有一个可拖动的UIView,最终这个UIView将成为一个可点击的按钮,但现在,我坚持更新这个视图的约束,当它被拖动屏幕。代码如下。动态/可拖动UIView的自动布局

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    self.anotherView = [[UIView alloc]init]; 
    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(dragging:)]; 
    [self.anotherView addGestureRecognizer:panRecognizer]; 
    [self.anotherView setBackgroundColor: [UIColor blueColor]]; 
    self.anotherView.translatesAutoresizingMaskIntoConstraints = NO; 

    [self.view addSubview: self.anotherView]; 
    [self.view bringSubviewToFront:self.anotherView]; 

    NSDictionary *views = @{@"subview" : self.anotherView, @"superview" : self.view}; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeLeft 
                  relatedBy:NSLayoutRelationLessThanOrEqual 
                  toItem:self.view 
                  attribute:NSLayoutAttributeLeft 
                 multiplier:1.0 
                  constant:0]]; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeCenterY 
                  relatedBy:NSLayoutRelationLessThanOrEqual 
                  toItem:self.view 
                  attribute:NSLayoutAttributeCenterY 
                 multiplier:1.0 
                  constant:0]]; 


    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeWidth 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:Nil 
                  attribute:NSLayoutAttributeWidth 
                 multiplier:1.0 
                  constant:20]]; 

    [self.view addConstraints:[NSLayoutConstraint 
           constraintsWithVisualFormat:@"V:[subview(40)]" 
           options:0 
           metrics:nil 
           views:views]]; 
     // add horizontal constraints 

    /*[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview(==20)]|" options:0 metrics:nil views:views]]; 

    // set the height of the offscreen subview to be the same as its superview 

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[subview(==40)]" options:0 metrics:nil views:views]]; 

    // set the location of the subview to be just off screen below the current view 

    NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.anotherView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:self.view.bounds.size.height]; 
    [self.view addConstraint:constraint];*/ 
     //[self.anotherView pinEdges:JRTViewPinLeftEdge toSameEdgesOfView:self.view]; 


     // Do any additional setup after loading the view, typically from a nib. 
    self.navigationItem.leftBarButtonItem = self.editButtonItem; 

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)]; 
    self.navigationItem.rightBarButtonItem = addButton; 
    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController]; 
} 

上面的代码是我如何在ViewController中设置UIView。预计规模为20x45。对于拖动代码如下:

-(void)dragging:(UIPanGestureRecognizer *)gesture 
{ 
    if(gesture.state == UIGestureRecognizerStateBegan) 
     { 
      //NSLog(@"Received a pan gesture"); 
     self.panCoord = [gesture locationInView:gesture.view]; 
     [self.tableView setScrollEnabled:NO]; 

     } 
    CGPoint newCoord = [gesture locationInView:gesture.view]; 
    float dX = newCoord.x-self.panCoord.x; 
    float dY = newCoord.y-self.panCoord.y; 
    CGFloat newXPoint; 
    CGFloat newYPoint; 

    gesture.view.frame = CGRectMake(gesture.view.frame.origin.x+dX, gesture.view.frame.origin.y+dY, gesture.view.frame.size.width, gesture.view.frame.size.height); 
    if (gesture.state == UIGestureRecognizerStateEnded) { 
     newYPoint = gesture.view.frame.origin.y+dY; 
     if (gesture.view.frame.origin.x+dX<(self.view.frame.size.width/2)) { 
      newXPoint = 0; 
     } 
     else { 
      newXPoint = self.view.frame.size.width - gesture.view.frame.size.width; 
//The below is just an attempt to remove and re-add constraints so the view will stick to the right side of the screen. It is not working :( 
      [self.view removeConstraint:[NSLayoutConstraint constraintWithItem:self.anotherView 
                    attribute:NSLayoutAttributeLeft 
                    relatedBy:NSLayoutRelationLessThanOrEqual 
                    toItem:self.view 
                    attribute:NSLayoutAttributeLeft 
                   multiplier:1.0 
                    constant:0]]; 

      [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.anotherView 
                    attribute:NSLayoutAttributeRight 
                    relatedBy:NSLayoutRelationLessThanOrEqual 
                    toItem:self.view 
                    attribute:NSLayoutAttributeRight 
                   multiplier:1.0 
                    constant:0]]; 


     } 


      //  self.draggedViewCoordinate = gesture.view.frame.origin; 

     [UIView beginAnimations:nil context:nil]; 
     [UIView setAnimationDuration:0.70]; 
     [UIView setAnimationDelay:0.0]; 
     [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; 
     [self.anotherView layoutIfNeeded]; 
     [UIView commitAnimations]; 
     [self.tableView setScrollEnabled:YES]; 
    } 

} 

我没有得到如何更新约束这样的观点会坚持新的拖累位置。任何人都可以帮忙吗?

接受的答案不正确。 UI添加/删除约束是非常昂贵的操作。 适当的解决方案将基于dx和dy来改变

self.yCoordinate.constant 

self.xCoordinate.constant 

性质。拖累当

好的,我用下面的代码解决了它!

-(void)dragging:(UIPanGestureRecognizer *)gesture 
{ 
    if(gesture.state == UIGestureRecognizerStateBegan) 
     { 
      //NSLog(@"Received a pan gesture"); 
     self.panCoord = [gesture locationInView:gesture.view]; 
     [self.tableView setScrollEnabled:NO]; 

     } 
    CGPoint newCoord = [gesture locationInView:gesture.view]; 
    float dX = newCoord.x-self.panCoord.x; 
    float dY = newCoord.y-self.panCoord.y; 
    CGFloat newXPoint; 
    CGFloat newYPoint; 
    gesture.view.frame = CGRectMake(gesture.view.frame.origin.x+dX, gesture.view.frame.origin.y+dY, gesture.view.frame.size.width, gesture.view.frame.size.height); 
    if (gesture.state == UIGestureRecognizerStateEnded) { 
     newYPoint = gesture.view.frame.origin.y+dY; 
     CGFloat newYPosition = (self.view.frame.size.height/2) - newYPoint - 20; 
     if (gesture.view.frame.origin.x+dX<(self.view.frame.size.width/2)) { 
      [self.view removeConstraint:self.xCoordinate]; 
      [self.view removeConstraint:self.yCoordinate]; 

      self.xCoordinate = [NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeLeft 
                  relatedBy:NSLayoutRelationLessThanOrEqual 
                   toItem:self.view 
                  attribute:NSLayoutAttributeLeft 
                  multiplier:1.0 
                  constant:0]; 

      [self.view addConstraint:self.xCoordinate]; 

      self.anotherView.translatesAutoresizingMaskIntoConstraints = NO; 

      self.yCoordinate = [NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeCenterY 
                  relatedBy:NSLayoutRelationEqual 
                   toItem:self.view 
                  attribute:NSLayoutAttributeCenterY 
                  multiplier:1.0 
                  constant:-newYPosition]; 
      [self.view addConstraint:self.yCoordinate]; 

     } 
     else { 
      newXPoint = self.view.frame.size.width - gesture.view.frame.size.width; 


      [self.view removeConstraint:self.xCoordinate]; 
      [self.view removeConstraint:self.yCoordinate]; 

      self.xCoordinate = [NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeRight 
                  relatedBy:NSLayoutRelationLessThanOrEqual 
                   toItem:self.view 
                  attribute:NSLayoutAttributeRight 
                  multiplier:1.0 
                  constant:0]; 

      [self.view addConstraint:self.xCoordinate]; 

      self.anotherView.translatesAutoresizingMaskIntoConstraints = NO; 

      self.yCoordinate = [NSLayoutConstraint constraintWithItem:self.anotherView 
                  attribute:NSLayoutAttributeCenterY 
                  relatedBy:NSLayoutRelationEqual 
                   toItem:self.view 
                  attribute:NSLayoutAttributeCenterY 
                  multiplier:1.0 
                  constant:-newYPosition]; 
      [self.view addConstraint:self.yCoordinate]; 



     } 


      //  self.draggedViewCoordinate = gesture.view.frame.origin; 

     [UIView beginAnimations:nil context:nil]; 
     [UIView setAnimationDuration:0.70]; 
     [UIView setAnimationDelay:0.0]; 
     [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; 
     [self.anotherView layoutIfNeeded]; 
       //  gesture.view.frame = CGRectMake(newXPoint, newYPoint, gesture.view.frame.size.width, gesture.view.frame.size.height); 
     [UIView commitAnimations]; 

     [self.tableView setScrollEnabled:YES]; 
    } 

} 

本质上,我保持对约束的引用。这帮助我根据拖动视图的位置删除/重新添加约束。

+6

我相信你可以改变约束的不变的性质,然后调用layoutIfNeeded – Ryan

这种简化版本解决了我和了起来:

@implementation SomeController 
{ 
    float beginDragPointY; 
    NSLayoutConstraint someConstraint; 
} 
- (void) viewDidLoad 
{ 
    // Define someConstraint 
} 

- (void) onDragGesture: (id) sender 
{ 
    UIPanGestureRecognizer* recognizer = (UIPanGestureRecognizer*)sender; 
    UIGestureRecognizerState state = recognizer.state; 
    CGPoint translation = [sender translationInView:self.view]; 

    switch(state) 
    { 
     case UIGestureRecognizerStateBegan: 
      NSLog(@"Translation began %f %f", translation.x, translation.y); 
      beginDragPointY = translation.y; 
     break; 

     case UIGestureRecognizerStateChanged: 
      showBasicEventAnnotationConstraint.constant += translation.y - beginDragPointY; 
      beginDragPointY = translation.y; 
      [self.view setNeedsLayout]; 
     break; 
     case UIGestureRecognizerStateEnded: 
     case UIGestureRecognizerStateCancelled: 
     case UIGestureRecognizerStateFailed: 
     case UIGestureRecognizerStatePossible: 
     default: 
      break; 
    } 

    [self.view layoutIfNeeded]; 
}