【问题标题】:Using view.bounds to make boundaries使用 view.bounds 来做边界
【发布时间】:2010-12-27 17:20:23
【问题描述】:

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

我目前有一个设置,其中CGRect 随手指移动而移动,但我不希望CGRect 超出viewbounds

原始方法

- (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];
    }
}

问题

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

当我可以轻松地 self.view.bounds 并获得边界 CGRect 时,这一切对我来说似乎很愚蠢。我如何利用bounds 来做到这一点?除了手动设置touchLocation 之外,有没有更好的方法来防止它发生?我可以以某种方式对其施加硬性限制吗?

switch 声明呢?这会比尝试的-ifs 更好吗?


尝试

我现在正在尝试使用下面发布的@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)

我还没弄明白,一发现就发到这里了。否则我会忘记更新,添加嘿。

我要回去看看在每种可能的情况下到底发生了什么。


错误

发现另一个问题,计算boundariessubtractsmaximums,但没有addminimums的代码。这很重要,因为这就是 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。应用数学约束:

首先,我们得到MAX 或最大值,根据我们的限制,minXstartTouchLocation 的最低可能值X。这将计算屏幕左侧的限制。结果设置为currentTouchLocation

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

其次,我们得到MIN 或最小值,根据我们的限制,maxXcurrentTouchLocation 的最高可能值X。请注意,我们没有使用原始触摸的位置,而是我们刚刚使用的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];
}

今天是学习的好日子。

【问题讨论】:

    标签: iphone objective-c cocoa-touch uikit boundary


    【解决方案1】:

    你肯定想使用它的 superview 的边界来约束它的移动。

    不要那么频繁地调用 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];
    }
    

    【讨论】:

    • 出于某种原因,如果我像你一样把它扔到最后,它就行不通了。不过谢谢,我很快就会尝试。
    • setupView 什么时候发生?它与 viewDidLoad 有何不同?
    • setupView 永远不会被自动调用。我的意思不是让您将该代码放置在您设置视图的任何位置。所以 viewDidLoad 是你想要放置它的地方。或从 viewDidLoad 或 viewWillAppear 调用 setupView:
    • 哦,好吧,我以为这是另一种内置方法。谢谢。
    • 嘿,Sneakyness,很高兴你解决了这个问题并很高兴能提供帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多