Я работаю над несколькими основными приложениями / утилитами / играми для iPhone, чтобы лучше справиться с ним.
В настоящее время у меня есть настройка, в которой CGRect
перемещается везде, куда движется палец, однако я не хочу, чтобы CGRect
выходил за пределы bounds
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 - фактическое начальное прикосновение.
Результат касания должен быть измененным результатом.
Приведенный выше журнал показывает его вне установленного 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)
Я не понял, я разместил здесь, как только обнаружил. В противном случае я бы забыл обновить вообще, ДОБАВИТЬ хе.
Я собираюсь вернуться и посмотреть, что именно происходит в каждой из возможных ситуаций.
Ошибка
Обнаружена еще одна проблема - код, который вычисляет boundaries
subtracts
из maximums
, но не add
до 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
. WTF?
Нижний левый угол:
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)
Как и ожидалось, он работал только для минимального значения. Я все еще очень озадачен тем, что эти функции должны делать. Проницательность будет высоко ценится!
РЕШИТЬ!
К счастью, наша длинная дорога подходит к концу. Итак, поскольку я понятия не имел, вот что делают MIN
и MAX
. Это не было изначально очевидно для меня из-за путаницы, которая имела место.
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
или самое высокое значение как самого низкого возможного значения в соответствии с нашими ограничениями, minX
, так и startTouchLocation
, для X
. Это вычисляет предел для левой стороны экрана. Результат этого установлен в currentTouchLocation
.
currentTouchLocation.x = MAX(minX, startTouchLocation.x);
Во-вторых, мы получаем MIN
, или самое низкое значение, как максимально возможного значения в соответствии с нашими ограничениями, maxX
, так и currentTouchLocation
, для 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];
}
Сегодня был хороший день для обучения.