Uso de la vista. Encuadrados para hacer límites
-
20-09-2019 - |
Pregunta
Estoy trabajando en algunas aplicaciones/utilidades/juegos básicos con el iPhone para obtener un mejor agarre.
Actualmente tengo una configuración donde un CGRect
se mueve donde sea que se mueva el dedo, sin embargo, no quiero el CGRect
para salir del bounds
del view
.
Método original
- (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];
}
}
Problema
He probado algunos métodos diferentes de esto, incluido el uso &&
y haciéndolo solo una declaración. los boundaries
obviamente están codificados, y no estoy seguro de que establezca manualmente el touchLocation
es la mejor manera de mantenerlo en bounds
.
Todo esto me parece una tontería cuando puedo fácilmente self.view.bounds
Y consigue la delantera CGRect
. ¿Cómo hago uso de bounds
¿para hacer esto? ¿Hay una mejor manera de evitar que ocurra aparte de la configuración? touchLocation
¿a mano? ¿Puedo darle un límite difícil de alguna manera?
Que tal un switch
¿declaración? ¿Eso funcionaría mejor que el intento?if
¿s?
Intentar
Ahora estoy intentando utilizar el método de @Brad Goss publicado a continuación, sin embargo, no funciona?
Touch Started: (319.000000,350.000000)
Touch Result: (319.000000,350.000000)
Boundaries calculated: X:(0.000000,288.000000) Y(0.000000,328.000000)
El tacto iniciado es el toque inicial real. El resultado táctil debe ser el resultado cambiado.
El registro anterior lo muestra fuera del definido bounds
, también conocido como no funciona. No voy a volver a publicar el código que escribió porque es exactamente el mismo.
Problema de intento
He descubierto el problema. Esta es la misma situación de tipo que anteriormente, pero con el registro de los valores individuales tal como se establecen.
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)
No lo he descubierto, lo publiqué aquí tan pronto como lo descubrí. De lo contrario, olvidaría actualizar en absoluto, agregar heh.
Voy a regresar y ver exactamente lo que está sucediendo en cada una de las posibles situaciones.
Error
Encontró otro problema, el código que calcula el boundaries
subtracts
desde el maximums
, pero no add
hacia minimums
. Esto es importante ya que esto es lo que el boundary
es para, para detener el rect
de salir de la pantalla.
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 */;
Con esto solucionado, puedo continuar trabajando en el problema original.
Error 2
Muy bien, he solucionado otro problema. Estaba agregando al y
valor cuando se mostraba. Olvidé agregarlo a este nuevo código/mencionarlo. El código ahora se ve así:
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*/;
El tamaño del círculo que se muestra está dentro de un 64px CGRect
, asi que 20px
fue suficiente para mostrar el círculo justo por encima del pulgar. Aunque esto se soluciona, todavía no ha ayudado a resolver nuestro problema original.
Esquina superior izquierda:
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)
Funciona como debería, pero estoy confundido sobre por qué los valores correctos están saliendo de MAX
en vez de MIN
. Algo se mezcla aquí.
Esquina superior derecha:
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)
Me impide salir de la parte superior de la pantalla, nuevamente de MAX
en vez de MIN
. Todavía puedo volar de la derecha, como se muestra arriba.
Esquina inferior derecha:
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)
Está fallando en ambas direcciones. Ahora los valores reales provienen de MAX
, y los valores máximos provienen de MIN
. WTF?
Esquina izquierda inferior:
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)
Como era de esperar, solo funcionó para el valor mínimo. Todavía estoy muy confundido sobre lo que se supone que deben hacer estas funciones. ¡La información sería muy apreciada!
¡Resuelto!
Nuestro largo camino llega a su fin, afortunadamente. Entonces, porque no tenía idea, esto es lo que MIN
y MAX
hacer. Inicialmente no era obvio para mí debido a la confusión que había tenido lugar.
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
Entonces, esto es lo que tiene que hacer para utilizar estas funciones correctamente en esta situación:
1. Calcule los valores máximos y mínimos para X e Y.
El valor más bajo para cada uno se calcula como
view.origin + keepOnScreenRect.width
El valor más alto de cada uno se calcula como
view.origin + view.size.height/width - keepOnScreenRect.height/width
¡No olvides el desplazamiento del dedo!
Si está haciendo lo que hice, y tenga la colocación de servicios de guardia ligeramente por encima del dedo del usuario, solo debe agregar el desplazamiento adicional al valor máximo de y, ya que el valor mínimo de y es la parte inferior de la pantalla/vista, que No puedes pasar físicamente más bajo que 0 AT.
Si se pregunta por qué se hace esto, es porque el usuario no puede ver a través de su dedo.
2. Obtenga la ubicación del toque 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. Aplicar las restricciones matemáticas:
Primero, obtenemos el MAX
, o valor más alto, tanto del valor más bajo posible según nuestros límites, minX
, y startTouchLocation
, por X
. Esto calcula el límite para el lado izquierdo de la pantalla. El resultado de esto se establece en currentTouchLocation
.
currentTouchLocation.x = MAX(minX, startTouchLocation.x);
Segundo, obtenemos el MIN
, o valor más bajo, tanto del valor más alto posible según nuestros límites, maxX
, y currentTouchLocation
, por X
. Tenga en cuenta que no estamos utilizando la ubicación del toque original, sino la ubicación resultante del MAX
función que acabamos de usar. Este valor ya se ha verificado para el lado izquierdo de la pantalla, por lo que verificamos la derecha.
currentTouchLocation.x = MIN(maxX, currentTouchLocation.x);
Que nos da un X
Ubicación garantizada para estar dentro de nuestros límites. Luego realizamos lo mismo en Y
.
currentTouchLocation.y = MAX(minY, startTouchLocation.y);
currentTouchLocation.y = MIN(maxY, currentTouchLocation.y);
Con todo esto terminado, ahora podemos decir la vista de volver a dibujar y ya no temer que nuestro pequeño rectángulo sea cortado de ninguna manera.
[self setNeedsDisplay];
Producto terminado
/* 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];
}
Hoy fue un buen día para aprender.
Solución
Definitivamente desea usar los límites de su supervisión para restringir su movimiento.
No llame a SetNeedSdisplay con tanta frecuencia. Llámalo una vez al final del método. Potencialmente estás volviendo a dibujar innecesariamente.
Usaría algo como lo siguiente para lograr esto.
// 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];
}