En utilisant view.bounds pour rendre les frontières
-
20-09-2019 - |
Question
Je travaille sur quelques applications de base / Utilitaires / jeux avec l'iPhone pour obtenir une meilleure prise sur elle.
J'ai actuellement une configuration où un CGRect
se déplace là où le doigt se déplace, mais je ne veux pas le CGRect
d'aller en dehors de la bounds
du view
.
Méthode originale
- (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];
}
}
Problème
J'ai essayé quelques méthodes différentes de celui-ci, y compris l'utilisation &&
et d'en faire une seule déclaration. Les boundaries
sont évidemment codé en dur, et je ne suis pas sûr que le réglage manuel du touchLocation
est la meilleure façon de le maintenir en bounds
.
Tout cela me semble stupide quand je peux facilement self.view.bounds
et obtenir le CGRect
englobante. Comment puis-je faire usage de bounds
pour le faire? Y at-il une meilleure façon de l'empêcher de se produire autre que la mise en touchLocation
manuellement? Puis-je placer en quelque sorte une limite stricte à ce sujet à la place?
Qu'en est-il une déclaration de switch
? Est-ce que travailler mieux que les tentatives de-if
s?
Tentative
Je tente maintenant d'utiliser la méthode de @Brad Goss affiché ci-dessous, mais il ne fonctionne pas?
Touch Started: (319.000000,350.000000)
Touch Result: (319.000000,350.000000)
Boundaries calculated: X:(0.000000,288.000000) Y(0.000000,328.000000)
tactile est en route la touche de départ réelle. Résultat contact doit être le résultat modifié.
Le journal ci-dessus montre à l'extérieur de la bounds
définie, alias ne fonctionne pas. Je ne vais pas reposter le code qu'il a écrit parce qu'il est exactement le même.
Tentative problème
Je l'ai découvert le problème. Ceci est la même situation de type comme ci-dessus, mais avec la journalisation pour les valeurs individuelles telles qu'elles sont définies.
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)
Je n'ai pas pensé à elle, je l'ai posté ici dès que je l'ai découvert. Sinon j'oublier de mettre à jour à tous, ADD heh.
Je vais revenir en arrière et voir exactement ce qui se passe dans chacune des situations possibles.
Erreur
trouvé un autre problème, le code qui calcule la boundaries
de subtracts
du maximums
, mais ne add
pas au minimums
. Ceci est important car c'est ce que le boundary
est pour, d'arrêter le rect
d'aller hors de l'écran.
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 */;
Avec ce fixe, je peux continuer à travailler sur le problème d'origine.
Erreur 2
D'accord, je l'ai résolu un autre problème. J'ajoutais à la valeur y
quand il est affiché. J'ai oublié de l'ajouter dans ce nouveau code / mentionner. Le code ressemble maintenant à ceci:
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*/;
La taille du cercle affiché est à l'intérieur d'un 64px CGRect
, donc 20px
était suffisante pour afficher le cercle juste au-dessus du pouce. Bien que ce soit fixé, il n'a toujours pas aidé à résoudre notre problème d'origine.
Coin supérieur gauche:
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)
Il fonctionne comme il se doit, mais je suis confus quant à la raison pour laquelle les valeurs correctes sortent de MAX
au lieu de MIN
. Quelque chose est mélangé ici.
coin supérieur droit:
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)
Il me empêche d'aller du haut de l'écran, encore une fois de MAX
au lieu de MIN
. Je suis encore capable de voler au large de la droite, comme indiqué ci-dessus.
coin en bas à droite:
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)
Il est à défaut dans les deux directions. Maintenant, les valeurs réelles proviennent de MAX
, et les valeurs maximales viennent MIN
. WTF?
coin en bas à gauche:
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)
On pouvait s'y attendre, il ne fonctionnait pour la valeur minimale. Je suis encore très confus quant à ce que ces fonctions sont censés faire. Aperçu serait très apprécié!
SOLVED!
Notre longue route tire à sa fin, heureusement. Donc, parce que je n'avais aucune idée, voici ce que font MIN
et MAX
. Il n'a pas été initially évident pour moi en raison de la confusion qui avait eu lieu.
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
Voilà donc ce que vous devez faire pour utiliser correctement ces fonctions dans cette situation:
1. Calculer le maximum et les valeurs minimales pour X et Y.
La valeur la plus basse pour chaque est calculée comme
view.origin + keepOnScreenRect.width
La valeur la plus élevée est chaque calculée comme étant de
view.origin + view.size.height/width - keepOnScreenRect.height/width
Ne pas oublier le décalage pour le doigt! Si vous faites ce que je faisais, et avez le keepOnScreenRect positionné légèrement au-dessus du doigt de l'utilisateur, vous ne devriez ajouterez le supplément offset à la valeur maximale de Y, comme la valeur minimale de Y est le bas de l'écran / vue, qui vous êtes physiquement incapable d'aller plus bas que 0 à.
Si vous vous demandez pourquoi cela est fait, il est parce que l'utilisateur ne peut pas voir à travers son / son doigt.
2. Obtenez l'emplacement du contact sur 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. Appliquer les contraintes mathématiques:
Tout d'abord, nous obtenons la MAX
, ou valeur la plus élevée, à la fois la plus petite valeur possible en fonction de nos limites, minX
et startTouchLocation
, pour X
. Cela calcule la limite pour le côté gauche de l'écran. Le résultat de ce paramètre est réglé currentTouchLocation
.
currentTouchLocation.x = MAX(minX, startTouchLocation.x);
En second lieu, nous obtenons la MIN
ou la valeur la plus faible, à la fois la valeur la plus élevée possible en fonction de nos limites, maxX
et currentTouchLocation
, pour X
. Notez que nous n'utilisons pas l'emplacement du contact d'origine, mais l'emplacement résultant de la fonction MAX
que nous venons d'utiliser. Cette valeur a déjà été vérifiée pour le côté gauche de l'écran, donc nous vérifier le droit.
currentTouchLocation.x = MIN(maxX, currentTouchLocation.x);
Cela nous donne un emplacement X
qui est garanti dans nos limites. Nous effectuons ensuite la même chose sur Y
.
currentTouchLocation.y = MAX(minY, startTouchLocation.y);
currentTouchLocation.y = MIN(maxY, currentTouchLocation.y);
Avec tout cela terminé, nous pouvons maintenant dire en vue de se redessiner et ne craignent plus avoir coupé notre joli petit rectangle de quelque façon.
[self setNeedsDisplay];
Produit fini
/* 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];
}
Aujourd'hui était une bonne journée pour l'apprentissage.
La solution
Vous voulez certainement utiliser les limites de celui-ci est superview de limiter son mouvement.
Ne pas appeler setNeedsDisplay souvent. Appelez une fois à la fin de la méthode. Vous redessinez potentiellement inutilement.
J'utiliser quelque chose comme ce qui suit pour y parvenir.
// 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];
}