Прямоугольник UIBezierPath с круглыми ручками

Я пытаюсь нарисовать прямоугольник с четырьмя круглыми ручками. Вот как бы это выглядело:

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

Круглые ручки «горячие». Другими словами, когда пользователь прикасается к нему, ручку можно перемещать, пока остальные точки закреплены. Я хотел знать, есть ли у кого-нибудь подход для кодирования этой функции. Я смотрю на UIBezierPath, чтобы нарисовать прямоугольник с кругами, но мне трудно думать о том, как разрешить пользователю касаться только кругов. Я думал, что может потребоваться пять разных объектов UIBezierPath, но в конечном итоге пользовательский интерфейс будет состоять из множества этих объектов.

Любые предложения будут ценны. Спасибо.


person Clay    schedule 12.02.2013    source источник
comment
А что насчет addArcWithCenter:radius:startAngle:endAngle:clockwise:? Вы знаете, когда остановить прямую ...   -  person Larme    schedule 12.02.2013


Ответы (2)


Я бы вообще не стал рисовать его как единую фигуру со сложными UIBezierPaths. Я бы подумал об этом как о 6 разных частях. Контейнер, прямоугольник и 4 круга.

У меня был бы простой контейнер UIView с прямоугольным видом и четырьмя круглыми UIViews по углам. Затем нанесите UIPanGestureRecognizer на каждый круг. В обработчике жестов переместите центр круга и отрегулируйте нижележащий прямоугольный прямоугольник на ту же величину. Это позволит избежать сложных путей или математики и упростит сложение и вычитание сумм на самом прямоугольнике.

Обновление: код!

Я создал автономный подкласс UIView, который обрабатывает все. Вы можете создать его так:

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)];

Сама рамка представления - это общая перетаскиваемая область. Выбранная рамка - это внутренний видимый прямоугольник.

//
//  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

А вот и реализация.

//
//  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

Это только проверка концепции. Есть много недостатков, таких как возможность перетаскивания вне коробки, сложности с мультитачем, отрицательные размеры. Со всеми этими проблемами можно справиться по-разному, и они являются секретом, благодаря которому нечто подобное превращается из красивой идеи в красивый пользовательский интерфейс. Я оставлю это на ваше усмотрение. :)

person Ryan Poolos    schedule 12.02.2013
comment
Мне очень нравится такой подход. Это кажется предельно простым. Видите ли вы какие-либо проблемы с производительностью при использовании этого подхода, когда на экране может одновременно отображаться несколько таких объектов? - person Clay; 12.02.2013
comment
Не ожидаю. Я решил добавить в него немного кода. Так что дайте мне 5 минут, и у меня будет небольшой пример, который вы можете попробовать. - person Ryan Poolos; 12.02.2013
comment
Добавлен код :) Надеюсь, это поможет вам начать работу. Пытался оставить полезные комментарии, но если у вас есть вопросы, не сомневайтесь. - person Ryan Poolos; 13.02.2013
comment
Это отличная отправная точка! Это делает перевод намного проще, чем иметь дело с несколькими слоями и т. Д. Огромный палец вверх! Если у меня возникнут другие вопросы, я отправлю их здесь. - person Clay; 13.02.2013
comment
Рад, что смог помочь, Удачного кодирования :) P.S. хотел бы увидеть некоторые из ваших решений проблем, о которых я упомянул. - person Ryan Poolos; 13.02.2013
comment
Я буду выкладывать их по мере продвижения! - person Clay; 13.02.2013
comment
давайте продолжим обсуждение в чате - person Clay; 13.02.2013

Вы захотите сохранить в своем классе круговые пути Безье, когда вы реализуете распознаватели жестов.

Существует документ Apple, описывающий, как реализовать UIView или UIControl, который принимает события касания с изображениями и образцом кода.

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

person Fruity Geek    schedule 12.02.2013