3

我正在尝试绘制一个具有四个圆形手柄的矩形。这是它的样子:

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

圆形把手是“热的”。换句话说,当用户触摸它时,手柄可以四处移动,而其余点则被锚定。我想知道是否有人可以编写此功能的代码。我正在查看 UIBezierPath 以绘制带圆圈的矩形,但我很难考虑如何允许用户仅点击圆圈。我在想它可能需要五个不同的 UIBezierPath 对象,但最终 UI 将由这些对象的多个组成。

任何建议将不胜感激。谢谢。

4

2 回答 2

4

我根本不会把它画成一个带有复杂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

这只是一个概念证明。有很多遗漏的注意事项,例如能够在框外拖动、多点触控复杂性、负尺寸。所有这些问题都可以以非常不同的方式处理,并且是使这样的事情从一个好主意变成一个漂亮的自定义界面的秘诀。我会把这部分留给你。:)

于 2013-02-12T19:40:41.033 回答
1

当您实现手势识别器时,您将希望将圆形贝塞尔路径存储在您的类中。

有一个 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

于 2013-02-12T19:38:37.273 回答