UIBezierPath Rectangle avec poignées circulaires

J'essaye de dessiner un rectangle qui a quatre poignées circulaires. Voici à quoi cela ressemblerait:

o----o | | | | o----o 

Les poignées circulaires sont "chaudes". En d'autres termes, lorsque l'user le touche, la poignée peut être déplacée pendant que le rest des points est ancré. Je voulais savoir si quelqu'un avait une approche pour coder cette fonctionnalité. Je regarde UIBezierPath pour dessiner le rectangle avec des cercles, mais j'ai du mal à penser comment permettre à l'user de taper seulement sur les cercles. Je pensais qu'il pourrait être nécessaire d'avoir cinq objects UIBezierPath différents, mais finalement l'interface user se composera de multiples de ces objects.

Toutes les suggestions seraient grandement appréciées. Merci.

Je ne dessinerais pas comme une forme unique avec UIBezierPath compliqué du tout. J'y penserais comme 6 pièces différentes. Un conteneur, un rectangle et 4 cercles.

Je voudrais avoir un conteneur UIView simple qui a une vue rectangle et quatre UIViews circulaires à ses coins. Ensuite, placez un UIPanGestureRecognizer sur chaque cercle. Dans le gestionnaire de mouvements, déplacez le centre du cercle et ajustez le rectangle sous-jacent de la même valeur. Cela évitera les paths compliqués ou les maths et rendra simple l'addition et la soustraction des montants sur le rectangle lui-même.

Mise à jour: Code!

J'ai créé une sous-class UIView autonome qui gère tout. Vous pouvez en créer un comme ceci:

 HandlesView *view = [[HandlesView alloc] initWithFrame:self.view.bounds]; [view setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth]; [view setBackgroundColor:[UIColor redColor]]; [self.view addSubview:view]; // A custom property that contains the selected area of the rectangle. Its updated while resizing. [view setSelectedFrame:CGRectMake(128.0, 128.0, 200.0, 200.0)]; 

Le cadre de la vue elle-même est la zone totale draggable. L'image sélectionnée est le rectangle visible intérieur.

 // // HandlesView.h // handles // // Created by Ryan Poolos on 2/12/13. // Copyright (c) 2013 Ryan Poolos. All rights reserved. // #import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> @interface HandlesView : UIView @property (nonatomic, readwrite) CGRect selectedFrame; @end 

Et voici la mise en œuvre.

 // // HandlesView.m // handles // // Created by Ryan Poolos on 2/12/13. // Copyright (c) 2013 Ryan Poolos. All rights reserved. // #import "HandlesView.h" @interface HandlesView () { UIView *rectangle; NSArray *handles; NSMutableArray *touchedHandles; UIView *circleTL; UIView *circleTR; UIView *circleBL; UIView *circleBR; } @end @implementation HandlesView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { rectangle = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 22.0, 22.0)]; [self addSubview:rectangle]; // Create the handles and position. circleTL = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0)]; [circleTL setCenter:CGPointMake(CGRectGetMinX(rectangle.frame), CGRectGetMinY(rectangle.frame))]; circleTR = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0)]; [circleTR setCenter:CGPointMake(CGRectGetMaxX(rectangle.frame), CGRectGetMinY(rectangle.frame))]; circleBL = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0)]; [circleBL setCenter:CGPointMake(CGRectGetMinX(rectangle.frame), CGRectGetMaxY(rectangle.frame))]; circleBR = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0)]; [circleBR setCenter:CGPointMake(CGRectGetMaxX(rectangle.frame), CGRectGetMaxY(rectangle.frame))]; handles = @[ circleTL, circleTR, circleBL, circleBR ]; for (UIView *handle in handles) { // Round the corners into a circle. [handle.layer setCornerRadius:(handle.frame.size.width / 2.0)]; [self setClipsToBounds:YES]; // Add a drag gesture to the handle. [handle addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]]; // Add the handle to the screen. [self addSubview:handle]; } } return self; } - (void)setSelectedFrame:(CGRect)selectedFrame { [rectangle setFrame:selectedFrame]; [circleTL setCenter:CGPointMake(CGRectGetMinX(rectangle.frame), CGRectGetMinY(rectangle.frame))]; [circleTR setCenter:CGPointMake(CGRectGetMaxX(rectangle.frame), CGRectGetMinY(rectangle.frame))]; [circleBL setCenter:CGPointMake(CGRectGetMinX(rectangle.frame), CGRectGetMaxY(rectangle.frame))]; [circleBR setCenter:CGPointMake(CGRectGetMaxX(rectangle.frame), CGRectGetMaxY(rectangle.frame))]; } - (CGRect)selectedFrame { return rectangle.frame; } // Forward the background color. - (void)setBackgroundColor:(UIColor *)backgroundColor { // Set the container to clear. [super setBackgroundColor:[UIColor clearColor]]; // Set our rectangle's color. [rectangle setBackgroundColor:[backgroundColor colorWithAlphaComponent:0.5]]; for (UIView *handle in handles) { [handle setBackgroundColor:backgroundColor]; } } - (void)handlePan:(UIPanGestureRecognizer *)gesture { // The handle we're moving. UIView *touchedHandle = gesture.view; // Keep track of touched Handles. if (!touchedHandles) { touchedHandles = [NSMutableArray array]; } switch (gesture.state) { case UIGestureRecognizerStateBegan: [touchedHandles addObject:touchedHandle]; break; case UIGestureRecognizerStateChanged: { CGPoint tranlation = [gesture translationInView:self]; // Calculate this handle's new center CGPoint newCenter = CGPointMake(touchedHandle.center.x + tranlation.x, touchedHandle.center.y + tranlation.y); // Move corresponding circles for (UIView *handle in handles) { if (handle != touchedHandle && ![touchedHandles containsObject:handle]) { // Match the handles horizontal movement if (handle.center.x == touchedHandle.center.x) { handle.center = CGPointMake(newCenter.x, handle.center.y); } // Match the handles vertical movement if (handle.center.y == touchedHandle.center.y) { handle.center = CGPointMake(handle.center.x, newCenter.y); } } } // Move this circle [touchedHandle setCenter:newCenter]; // Adjust the Rectangle // The origin and just be based on the Top Left handle. float x = circleTL.center.x; float y = circleTL.center.y; // Get the width and height based on the difference between handles. float width = abs(circleTR.center.x - circleTL.center.x); float height = abs(circleBL.center.y - circleTL.center.y); [rectangle setFrame:CGRectMake(x, y, width, height)]; [gesture setTranslation:CGPointZero inView:self]; } break; case UIGestureRecognizerStateEnded: [touchedHandles removeObject:touchedHandle]; break; default: break; } } @end 

Ceci est seulement une preuve de concept. Il y a beaucoup de mises en garde manquantes comme être capable de faire glisser hors de la boîte, les complications multitouch, les tailles négatives. Tous ces problèmes peuvent être traités très différemment et sont la sauce secrète qui fait quelque chose comme ça va d'une idée sympa à une belle interface personnalisée. Je vais laisser cette partie à vous. 🙂

Vous voulez stocker les paths Bézier cercle dans votre class pour quand vous implémentez les reconnaisseurs de gestes.

Il existe un document Apple décrivant comment implémenter un UIView ou UIControl qui accepte les events tactiles avec des images et un exemple de code.

http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/multitouch_background/multitouch_background.html#//apple_ref/doc/uid/TP40009541-CH5-SW9