Arrastre el separador para cambiar el tamaño de UIViews.

¿Cuál sería la mejor manera de implementar una interfaz que consiste en UIViews que están separadas por una línea y la línea puede cambiar el tamaño de las vistas?

En su forma más simple, podría verse así:

---------------- | | | View A | | | |--------------| < line which can be moved up and down, resizing the views | | | View B | | | ---------------- 

Podría tener muchas más vistas.

Mi primer pensamiento sería hacer que la línea sea una vista UIV arrastrable con algo así como Touches , que cambió el tamaño de las vistas según su position, pero estoy seguro de que debe haber una solución más elegante.

Primero, defina un gesto que detecte si comenzó en un borde y, si el gesto cambia, mueve dichos bordes:

 #import <UIKit/UIGestureRecognizerSubclass.h> - (void)viewDidLoad { [super viewDidLoad]; // I use long press gesture recognizer so it's recognized immediately UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; gesture.minimumPressDuration = 0.0; gesture.allowableMovement = CGFLOAT_MAX; gesture.delegate = self; [self.containerView addGestureRecognizer:gesture]; } - (void)handlePan:(UILongPressGestureRecognizer *)gesture { static NSArray *matches; static CGPoint firstLocation; if (gesture.state == UIGestureRecognizerStateBegan) { firstLocation = [gesture locationInView:gesture.view]; matches = [BorderBeingDragged findBordersBeingDraggedForView:gesture.view fromLocation:firstLocation]; if (!matches) { gesture.state = UIGestureRecognizerStateFailed; return; } } else if (gesture.state == UIGestureRecognizerStateChanged) { CGPoint location = [gesture locationInView:gesture.view]; CGPoint translation = CGPointMake(location.x - firstLocation.x, location.y - firstLocation.y); [BorderBeingDragged dragBorders:matches translation:translation]; } } // if your subviews are scrollviews, you might need to tell the gesture recognizer // to allow simultaneous gestures - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return TRUE; } 

En segundo lugar, defina una class BordersBeingDragged que realiza la detección de bordes y el cambio de bordes:

 typedef enum NSInteger { kBorderTypeNone = 0, kBorderTypeLeft = 1 << 0, kBorderTypeRight = 1 << 1, kBorderTypeTop = 1 << 2, kBorderTypeBottom = 1 << 3 } BorderType; @interface BorderBeingDragged : NSObject @property (nonatomic, weak) UIView *view; @property (nonatomic) BorderType borderTypes; @property (nonatomic) CGRect originalFrame; @end static CGFloat const kTolerance = 15.0; @implementation BorderBeingDragged + (NSArray *)findBordersBeingDraggedForView:(UIView *)view fromLocation:(CGPoint)point { NSMutableArray *matches = nil; for (UIView *subview in view.subviews) { BorderType types = kBorderTypeNone; CGRect frame = subview.frame; // test top and bottom borders if (point.x >= (frame.origin.x - kTolerance) && point.x <= (frame.origin.x + frame.size.width + kTolerance)) { if (point.y >= (frame.origin.y - kTolerance) && point.y <= (frame.origin.y + kTolerance)) types |= kBorderTypeTop; else if (point.y >= (frame.origin.y + frame.size.height - kTolerance) && point.y <= (frame.origin.y + frame.size.height + kTolerance)) types |= kBorderTypeBottom; } // test left and right borders if (point.y >= (frame.origin.y - kTolerance) && point.y <= (frame.origin.y + frame.size.height + kTolerance)) { if (point.x >= (frame.origin.x - kTolerance) && point.x <= (frame.origin.x + kTolerance)) types |= kBorderTypeLeft; else if (point.x >= (frame.origin.x + frame.size.width - kTolerance) && point.x <= (frame.origin.x + frame.size.width + kTolerance)) types |= kBorderTypeRight; } // if we found any borders, add it to our array of matches if (types != kBorderTypeNone) { if (!matches) matches = [NSMutableArray array]; BorderBeingDragged *object = [[BorderBeingDragged alloc] init]; object.borderTypes = types; object.view = subview; object.originalFrame = frame; [matches addObject:object]; } } return matches; } + (void)dragBorders:(NSArray *)matches translation:(CGPoint)translation { for (BorderBeingDragged *object in matches) { CGRect newFrame = object.originalFrame; if (object.borderTypes & kBorderTypeLeft) { newFrame.origin.x += translation.x; newFrame.size.width -= translation.x; } else if (object.borderTypes & kBorderTypeRight) { newFrame.size.width += translation.x; } if (object.borderTypes & kBorderTypeTop) { newFrame.origin.y += translation.y; newFrame.size.height -= translation.y; } else if (object.borderTypes & kBorderTypeBottom) { newFrame.size.height += translation.y; } object.view.frame = newFrame; } } @end 

Esencialmente, necesita hacer que la vista de línea sea arrastrable, pero no tiene por qué ser complicada.

  1. Ponga viewA y viewB en un containerView
  2. Agregue un reconocedor de gestos panorámico al containerView configurado con un solo toque y configure su delegado en su controller.
  3. Implementar gestureRecognizerShouldBegin: desde el protocolo UIGestureRecognizerDelegate y solo permitir que comience si se toca en las proximidades de la vista de línea.
  4. En el gestor de gestos, obtenga la position de toque en el containerView y establezca la position de vista de línea y los frameworks para la viewA y la viewB

Eso es practicamente todo.

Sugiero otro comportamiento: 1. Presiona y mantén presionado 2 segundos en la línea 2. Aparece alguna image que arrastrarás

la forma más simple es agregar reconocimiento de gestos a sus vistas y cambiar su tamaño según la panorámica.