Pergunta

Estou trabalhando em alguns aplicativos/utilitários/jogos básicos com o iPhone para entender melhor.

Atualmente tenho uma configuração onde um CGRect move onde quer que o dedo se mova, no entanto, eu não quero o CGRect para sair do bounds do 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

Eu tentei alguns métodos diferentes disso, incluindo o uso && E fazendo isso apenas uma afirmação. o boundaries são obviamente codificados e não tenho certeza de que definir manualmente o touchLocation é a melhor maneira de mantê -lo em bounds.

Tudo isso me parece bobo quando eu puder facilmente self.view.bounds e pegue o limite CGRect. Como faço uso de bounds para fazer isso? Existe uma maneira melhor de impedir que isso aconteça além de definir touchLocation manualmente? De alguma forma, posso colocar um limite rígido nele?

Que tal a switch declaração? Isso funcionaria melhor do que a tentativaifs?


Tentar

Agora estou tentando utilizar o método do @Brad Goss publicado abaixo, no entanto, ele não está funcionando?

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

O toque iniciado é o toque inicial real. O resultado do toque deve ser o resultado alterado.

O log acima está mostrando -o fora do definido bounds, também conhecido como não está funcionando. Não vou repassar o código que ele escreveu porque é exatamente o mesmo.


Problema de tentativa

Eu descobri o problema. Esta é a mesma situação do tipo acima, mas com o log para os valores individuais que estão definidos.

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)

Eu não descobri, publiquei aqui assim que descobri. Caso contrário, eu esqueceria de atualizar, acrescentando heh.

Vou voltar e ver exatamente o que está acontecendo em cada uma das possíveis situações.


Erro

Encontrou outro problema, o código que calcula o boundaries subtracts de maximums, mas não add para o minimums. Isso é importante, pois é isso boundary é para, para parar o rect de sair da tela.

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 */;

Com isso fixo, posso continuar trabalhando no problema original.


Erro 2

Tudo bem, eu resolvi outro problema. Eu estava adicionando ao y valor quando estava sendo exibido. Esqueci de adicioná -lo a este novo código/mencioná -lo. O código agora se parece com o seguinte:

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*/;

O tamanho do círculo exibido está dentro de um 64px CGRect, assim 20px foi suficiente para exibir o círculo logo acima do polegar. Embora isso seja corrigido, ainda não ajudou a resolver nosso problema original.

Canto superior esquerdo:

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 deveria, mas estou confuso sobre por que os valores corretos estão saindo de MAX ao invés de MIN. Algo está misturado aqui.

Canto superior direito:

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)

Está me impedindo de sair do topo da tela, novamente de MAX ao invés de MIN. Ainda sou capaz de voar para a direita, como mostrado acima.

Canto inferior direito:

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á falhando nas duas direções. Agora os valores reais vêm de MAX, e os valores máximos vêm de MIN. Wtf?

Canto inferior esquerdo:

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)

Previsivelmente, ele só funcionou pelo valor mínimo. Ainda estou muito confuso sobre o que essas funções devem fazer. O insight seria muito apreciado!


Resolvido!

Nossa longa estrada chega ao fim, felizmente. Então, porque eu não tinha idéia, aqui está o que MIN e MAX Faz. Inicialmente não era óbvio para mim devido à confusão que havia ocorrido.

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

Então é isso que você precisa fazer para usar essas funções corretamente nesta situação:

1. Calcule os valores máximos e mínimos para x e y.

O menor valor para cada um é calculado como

view.origin + keepOnScreenRect.width  

O valor mais alto de cada um é calculado como

view.origin + view.size.height/width - keepOnScreenRect.height/width

Não se esqueça do deslocamento do dedo!
Se você estiver fazendo o que eu fiz e tem o keeptonscreenRect posicionado um pouco acima do dedo do usuário, você deve adicionar apenas o deslocamento extra ao valor máximo de y, pois o valor mínimo de y é a parte inferior da tela/exibição, que Você é fisicamente incapaz de ir mais baixo que 0 em.

Se você está se perguntando por que isso é feito, é porque o usuário não pode ver através do dedo dele.

2. Obtenha a localização do 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. Aplique as restrições matemáticas:

Primeiro, temos o MAX, ou valor mais alto, de ambos o menor valor possível de acordo com nossos limites, minX, e startTouchLocation, por X. Isso calcula o limite para o lado esquerdo da tela. O resultado disso está definido como currentTouchLocation.

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

Segundo, temos o MIN, ou menor valor, do valor mais alto possível de acordo com nossos limites, maxX, e currentTouchLocation, por X. Observe que não estamos usando a localização do toque original, mas o local resultante do MAX função que acabamos de usar. Esse valor já foi verificado para o lado esquerdo da tela, então verificamos a direita.

currentTouchLocation.x = MIN(maxX, currentTouchLocation.x);

Isso nos dá um X Localização que é garantida estar dentro de nossos limites. Em seguida, realizamos o mesmo em Y.

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

Com tudo isso terminado, agora podemos dizer a vista para se desviar e não tem mais medo de ter nosso adorável retângulo cortado de qualquer forma.

[self setNeedsDisplay];

Produto final

/* 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];
}

Hoje foi um bom dia para aprender.

Foi útil?

Solução

Você definitivamente deseja usar os limites de sua supervisão para restringir seu movimento.

Não chame o setNeedsDisplay com frequência. Chame uma vez no final do método. Você está potencialmente redesenhando desnecessariamente.

Eu usaria algo como o seguinte para conseguir isso.

// 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];
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top