使用view.bounds来设置边界

4

我正在开发一些基本的iPhone应用程序/实用工具/游戏,以更好地掌握它。

目前,我已经设置了一个CGRect,可以随着手指移动而移动。然而,我不希望CGRect超出view的边界。

原始方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    startLocation = [touch locationInView:self];
    if (startLocation.x < 33){
        touchLocation.x = 33;
            //NSLog(@"Touch At:%f, %f", touchLocation.x, touchLocation.y);
        [self setNeedsDisplay];
    }
    if (startLocation.x > 288){
        touchLocation.x = 288;
            //NSLog(@"Touch At:%f, %f", touchLocation.x, touchLocation.y);
        [self setNeedsDisplay];
    }
    if (startLocation.y < 84){
        touchLocation.y = 84;
            //NSLog(@"Touch At:%f, %f", touchLocation.x, touchLocation.y);
        [self setNeedsDisplay];
    }
    if (startLocation.y > 460){
        touchLocation.y = 460;
            //NSLog(@"Touch At:%f, %f", touchLocation.x, touchLocation.y);
        [self setNeedsDisplay];
    }
    else {
        touchLocation = startLocation;
        [self setNeedsDisplay];
    }
}

问题

我已经尝试了几种不同的方法,包括使用&&并将其作为一个语句。显然,boundaries是硬编码的,我不确定手动设置touchLocation是否是保持在bounds中的最佳方式。

当我可以轻松地使用self.view.bounds并获取边界CGRect时,所有这些看起来都很荒谬。如何利用bounds来做到这一点?除了手动设置touchLocation之外,有没有更好的方法来防止它发生?我能否在其上放置硬限制?

那么switch语句呢?它是否比尝试的if更有效?


尝试

我现在正在尝试利用@Brad Goss下面发布的方法,但它不起作用?

Touch Started: (319.000000,350.000000)
Touch Result: (319.000000,350.000000)
Boundaries calculated: X:(0.000000,288.000000) Y(0.000000,328.000000)

Touch Started是实际的开始触摸。

Touch Result应该是改变后的结果。

上面的日志显示在定义的bounds之外,也就是没有起作用。我不会重新发布他编写的代码,因为它完全相同。


尝试问题

我已经发现了问题。这是与上述情况相同的类型,但记录了设置每个值时的单独值。

Touch Started: (293.000000,341.000000)
min x:288.000000
max x:293.000000
min y:328.000000
max y:341.000000
Touch Result: (293.000000,341.000000)
Boundaries calculated: X:(0.000000,288.000000) Y(0.000000,328.000000)

我还没想明白,一发现问题就在这里发布了。否则我会完全忘记更新,ADD 呵呵。

我将回去查看每种可能情况下的具体情况。


错误

发现另一个问题,计算boundaries的代码减去maximums,但没有加上minimums。这很重要,因为这是boundary的作用,用于防止rect离开屏幕。

maxX = b.origin.x + b.size.width/* - [box bounds].size.width */;
minX = b.origin.x/* + [box bounds].size.width */;
maxY = b.origin.y + b.size.height/* - [box bounds].size.height */;
minY = b.origin.y?/* + [box bounds].size.height */;

问题已经解决,我现在可以继续处理原来的问题。


错误2

好了,我又解决了一个问题。之前在显示时我对 y 值进行了累加,但是我忘记在这个新代码中添加/提及了它。现在代码看起来像这样:

maxX = b.origin.x + b.size.width/* - [box bounds].size.width */;
minX = b.origin.x/* + [box bounds].size.width */;
maxY = b.origin.y + b.size.height/* - [box bounds].size.height + 20*/;
minY = b.origin.y/* + [box bounds].size.height + 20*/;

显示的圆圈大小在一个64px CGRect内,因此20px足以将圆圈显示在拇指上方。虽然这是固定的,但仍未解决我们最初的问题。

左上角:

Touch Started: (14.000000,20.000000)
min x:14.000000
max x:32.000000
min y:20.000000
max y:84.000000
Touch Result: (32.000000,84.000000)
Boundaries calculated: X:(32.000000,288.000000) Y(84.000000,276.000000)

它按照预期工作,但我不明白为什么正确的值来自于MAX而不是MIN。这里有些混淆。

右上角:

Touch Started: (303.000000,21.000000)
min x:288.000000
max x:303.000000
min y:21.000000
max y:84.000000
Touch Result: (303.000000,84.000000)
Boundaries calculated: X:(32.000000,288.000000) Y(84.000000,276.000000)

这阻止了我从屏幕顶部飞出,而是使用MAX而不是MIN。如上图所示,我仍然能够飞到右边。

右下角:

Touch Started: (317.000000,358.000000)
min x:288.000000
max x:317.000000
min y:276.000000
max y:358.000000
Touch Result: (317.000000,358.000000)
Boundaries calculated: X:(32.000000,288.000000) Y(84.000000,276.000000)

它在两个方向上都失败了。现在实际值来自MAX,最大值来自MIN。真是令人困惑?

左下角:

Touch Started: (8.000000,354.000000)
min x:8.000000
max x:32.000000
min y:276.000000
max y:354.000000
Touch Result: (32.000000,354.000000)
Boundaries calculated: X:(32.000000,288.000000) Y(84.000000,276.000000)

可预见的是,它只适用于最小值。我仍然非常困惑这些函数应该做什么。希望能得到更多的见解!

已解决!

我们漫长的道路即将结束,谢天谢地。所以,因为我一点也不知道,这就是MINMAX的作用。由于发生的混乱,初始时对我来说并不明显。

MAX(x,y) will output the larger of the two numbers
// MAX(13,37) = 37
MIN(x,y) will output the smallest of the two numbers
// MIN(13,37) = 13

如果您想在这种情况下正确使用这些功能,需要执行以下操作:

1. 计算X和Y的最大值和最小值。

每个值的最低值计算如下:

view.origin + keepOnScreenRect.width  

每个值的最高值计算如下:
view.origin + view.size.height/width - keepOnScreenRect.height/width

不要忘记手指的偏移量!
如果您正在执行我所做的操作,并且将keepOnScreenRect位置略微放在用户手指上方,则应仅将额外偏移添加到Y的最大值中,因为Y的最小值是屏幕/视图的底部,您无法物理上低于0。

如果您想知道为什么要这样做,那是因为用户无法透过自己的手指看到屏幕。

2. 在touchesBegan:中获取触摸位置

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    startTouchLocation = [touch locationInView:self];
    NSLog(@"Touch Started: (%f,%f)", startTouchLocation.x, startTouchLocation.y);

3. 应用数学约束:

首先,我们获取根据限制条件得到的最小可能值 minXX 的触摸起始位置 startTouchLocation 中的最高值(即MAX),用于计算屏幕左边界的限制。将此结果设置为 currentTouchLocation

currentTouchLocation.x = MAX(minX, startTouchLocation.x);

其次,我们获取 X 的最低值,即根据限制条件的最大可能值 maxX 和当前触摸位置 currentTouchLocation 中的最小值 MIN。请注意,我们不是使用原始触摸位置,而是使用刚刚使用的 MAX 函数的结果位置。该值已经检查过屏幕左侧,因此我们要检查右侧。
currentTouchLocation.x = MIN(maxX, currentTouchLocation.x);

这给我们一个保证在限制范围内的 X 位置。然后我们对 Y 执行相同的操作。

currentTouchLocation.y = MAX(minY, startTouchLocation.y);
currentTouchLocation.y = MIN(maxY, currentTouchLocation.y);

完成以上所有步骤后,我们现在可以告诉视图重新绘制自己,并不再担心我们可爱的小矩形被任何方式裁剪。

[self setNeedsDisplay];

成品

/* Generates the limits based on the view's size, taking into account 
   the width/height of the rect being displayed in it. This should only
   be run once, unless the size of the view changes, in which case limits
   would have to be recalculated. The same goes for if you were to change
   the size of the displaying rect after calculation */

- (void)boundsCalculation {
    CGRect viewBounds = [self bounds];

        // calculate constraints
    maxX = viewBounds.origin.x + viewBounds.size.width - 32;
    minX = viewBounds.origin.x + 32;
    maxY = viewBounds.origin.y + viewBounds.size.height - 32;
    minY = viewBounds.origin.y + 84;
    NSLog(@"Boundaries calculated: X:(%f,%f) Y(%f,%f)", minX, maxX, minY, maxY);
}

/* Magic goodness that happens when the user touches the screen.
   Note that we don't calculate the bounds every time the user touches
   the screen, that would be silly. */

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    startTouchLocation = [touch locationInView:self];
    NSLog(@"Touch Started: (%f,%f)", startTouchLocation.x, startTouchLocation.y);

        // apply constraints
            // highest x value between the lowest allotted x value and touch
    currentTouchLocation.x = MAX(minX, startTouchLocation.x);
            // lowest x value between the highest allotted x value and touch
    currentTouchLocation.x = MIN(maxX, currentTouchLocation.x);
            // highest y value between the lowest allotted y value and touch
    currentTouchLocation.y = MAX(minY, startTouchLocation.y);
            // lowest y value between the highest allotted y value and touch
    currentTouchLocation.y = MIN(maxY, currentTouchLocation.y);
           // NSLog(@"Touch Result: (%f,%f)", currentTouchLocation.x, currentTouchLocation.y);

           // If you don't do this you won't see anything
    [self setNeedsDisplay];
}

今天是一个很好的学习日。

1个回答

2

您肯定希望使用其父视图的边界来限制其移动。

不要频繁调用setNeedsDisplay方法。在方法结束时只需调用一次即可。否则可能会不必要地重绘。

我建议使用以下代码来实现此目的。

// define these variables where ever you'd like
static float maxX = 0.;
static float maxY = 0.;
static float minX = 0.;
static float minY = 0.;

- (void)setupView {
    CGRect b = [self bounds];

    // calculate constraints
    maxX = b.origin.x + b.size.width/* - [box bounds].size.width */;
    minX = b.origin.x;
    maxY = b.origin.y + b.size.height/* - [box bounds].size.height */;
    minY = b.origin.y;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    startLocation = [touch locationInView:self];

    // apply constraints
    touchLocation.x = MIN(maxX, startLocation.x);
    touchLocation.x = MAX(minX, startLocation.x);
    touchLocation.y = MIN(maxY, startLocation.y);
    touchLocation.y = MAX(minY, startLocation.y);

    [self setNeedsDisplay];
}

由于某种原因,如果我像你那样把它扔到最后,它就不会工作。不过还是谢谢,我很快就会尝试这个方法。 - Sneakyness
setupView是在什么时候发生的?它与viewDidLoad有何不同? - Sneakyness
1
setupView不会自动调用。我的意思是让你把那段代码放在设置视图的任何地方。所以viewDidLoad是你想要放置它的地方。或者从viewDidLoad或viewWillAppear中调用setupView。 - Brad G
好的,我以为这是另一个内置方法。谢谢。 - Sneakyness
我现在已经弄清楚了一切!感谢你的启发!你忘记在每个函数的第一个后使用计算出来的值了。 - Sneakyness
1
嘿,Sneakyness,很高兴你解决了问题,也很高兴能帮到你。 - Brad G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接