тест на попадание в слой, возвращающий слой только при касании нижней половины слоя

У меня есть подслой на просмотре с поддержкой слоев. Содержимое подслоя установлено на Image Ref и представляет собой прямоугольник 25x25.
Я провожу проверку совпадения на суперслое, когда вызываются методы touchesBegan и touchesMoved. Фактически, метод проверки попадания возвращает подслой при прикосновении, НО только при прикосновении к нижней половине изображения. Если прикоснуться к верхней половине изображения, вместо этого будет возвращен суперслой.

Я знаю, что iPhone OS компенсирует тенденцию к тому, чтобы пользовательские прикосновения были ниже, чем предполагалось. Даже если я изменю размер подслоя до большего размера, 50x50, он будет вести себя так же.

Есть предположения?


person Corey Floyd    schedule 30.12.2008    source источник


Ответы (8)


В документации для HitTest говорится:

/* Returns the farthest descendant of the layer containing point 'p'.
 * Siblings are searched in top-to-bottom order. 'p' is in the
 * coordinate system of the receiver's superlayer. */

Итак, вам нужно сделать (примерно так):

CGPoint thePoint = [touch locationInView:self];
thePoint = [self.layer convertPoint:thePoint toLayer:self.layer.superlayer];
CALayer *theLayer = [self.layer hitTest:thePoint];

(Повторяющийся ответ из другого сообщения для полноты)

person schwa    schedule 25.02.2009
comment
Спасибо! У меня была такая же проблема, и она сводила меня с ума. Я все еще немного не понимаю, зачем это нужно, но, похоже, он работает. - person Mark Bessey; 21.03.2009
comment
Я думаю, что одна из проблем заключается в том, что координаты представления CA переворачиваются из координат UIView (внизу слева - начало координат)? Но я хотел бы услышать от кого-то, кто может подробно рассказать о представлении и иерархии слоев и действительно объяснить, что еще происходит. - person Pat Niemeyer; 15.07.2010
comment
Этот ответ сэкономил мне много времени и скорби - спасибо! Я все еще кое-что не понимаю: если self - это представление, получившее касание, то self.layer - соответствующий ему слой - тогда что такое self.layer.superlayer? - person wcochran; 16.04.2012

Кажется, что слой не просто касается нижних пикселей. Вместо этого кажется, что «фактический» экранный слой и его содержимое определяются разными CGRects. Изображение отображается в ожидаемых координатах, а слой, который реагирует на прикосновения, смещен под изображением. Под ниже я подразумеваю, что источник изображения находится в (200, 200), а источник слоя, который реагирует на прикосновения, находится в (200, 220).

Ниже я публикую тестовый код, который использовал для воссоздания проблемы. Сначала мой подкласс представления, а затем мой контроллер представления. Мы очень ценим любые аргументы в пользу этой проблемы.

Подкласс My View:

#import "myView.h"
#import <QuartzCore/QuartzCore.h>


@implementation myView


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    CGPoint currentLocation = [[touches anyObject] locationInView:self];

    CALayer *currentLayer = [self.layer hitTest:currentLocation];

    CGPoint center = [currentLayer position];

    NSLog([currentLayer valueForKey:@"isRootLayer"]);

    if(![currentLayer valueForKey:@"isRootLayer"]) {

        if (center.x != 200) {

            [currentLayer setPosition:CGPointMake(200.0f, 200.0f)];     

        } else {
            [currentLayer setPosition:CGPointMake(100.0f, 100.0f)];     

        }
    }
}


- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        // Initialization code
    }
    return self;
}


- (void)drawRect:(CGRect)rect {
    // Drawing code
}


- (void)dealloc {
    [super dealloc];
}


@end

Контроллер моего представления:

#import "layerTestViewController.h"
#import <QuartzCore/QuartzCore.h>


#define ELEMENT_PERIOD_SIZE 50
#define ELEMENT_GROUP_SIZE 50

@implementation layerTestViewController





// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    CALayer  *myLayer = [CALayer layer];
    CGRect layerFrame =CGRectMake(0.0f, 0.0f, ELEMENT_GROUP_SIZE, ELEMENT_PERIOD_SIZE);
    myLayer.frame = layerFrame; 

    [myLayer setName:[NSString stringWithString:@"test"]];
    [myLayer setValue:[NSString stringWithString:@"testkey"] forKey:@"key"];

    UIGraphicsBeginImageContext(layerFrame.size);
    [[UIColor blueColor] set];
    UIRectFill(layerFrame);
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    myLayer.contents = (id)[theImage CGImage];
    myLayer.position = CGPointMake(100.0f, 100.0f);

    [self.view.layer addSublayer:myLayer];   

    [self.view.layer setValue:[NSString stringWithString:@"YES"] forKey:@"isRootLayer"];
    NSLog([self.view.layer valueForKey:@"isRootLayer"]);

}




- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [super dealloc];
}

@end
person Corey Floyd    schedule 05.01.2009

Принятый ответ не решает проблему, но ответ schwa решает.

У меня есть окно, контроллер представления и представление. Представление получает касания и вызывает hitTest: на своем слое. У меня была та же проблема, и я обнаружил, что точка в представлении правильная, но в hitTest: координата y точки отклонена на 20 пикселей, что является высотой строки состояния.

Путем сопоставления точки с системой координат суперслоя, как предлагает schwa, эта проблема решается. «Таинственный» суперслой является корневым слоем супервизора. В моем случае это слой окна (кадр (0, 0, 320, 480)).

person Raginmari    schedule 21.05.2012

Если касания распознаются только в нижней части, одна из возможностей состоит в том, что другое подчиненное представление покрывает верхнюю половину этого подчиненного представления. У вас несколько подвидов или только одно изображение? Если у вас есть несколько вложенных представлений, и у любого из них цвет фона clearColor, попробуйте дать ему сплошной цвет фона для тестирования. Таким образом вы узнаете, покрывается ли ваше подпредставление другим подпредставлением или нет.

Надеюсь, это поможет.

person lostInTransit    schedule 05.01.2009

Я упростил свой код, чтобы найти проблему. У меня на экране только один подслой и нет никаких подвидов. Приведенный выше код - это то, что я использую в симуляторе. Поскольку на экране всего 2 слоя, слой поддержки основного представления и добавленный мной подслой, ничто не должно мешать проверке попадания.

В случае, если это было неясно, при касании верхней части подслоя вместо этого проверка попадания возвращает слой поддержки. Также, если я касаюсь заднего слоя в точке чуть ниже подслоя, этот подслой возвращается проверкой попадания.

Это сложно объяснить, но если запустить код, все станет ясно.

Спасибо

person Corey Floyd    schedule 05.01.2009

Спасибо, Кевин ...

В итоге я просто переделал свой код с помощью UIViews. Было слишком сложно просто понять, почему UIView не знает, какой слой был затронут. Все мои доводы в пользу этой идеи заключались в том, что у меня будет одновременно отображаться более 100 элементов на экране, и я думал, что слои будут проще для процессора, чем UIViews. У меня есть приложение, которое работает, и 100 просмотров не вызывают никаких проблем.

Я предлагаю другим просто придерживаться проверки UIViews, когда это возможно, это значительно упрощает код.

person Corey Floyd    schedule 20.01.2009

/// Добавить frame.origin.y

    public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let convertPoint = CGPoint(x:point.x, y:(point.y + frame.origin.y))
        let superPiont = self.layer.convert(convertPoint, to:layer.superlayer)
        selectlayer = layer.superlayer?.hitTest(superPiont)
        }
person Sico2 Sico    schedule 10.04.2020

Когда я пробую ваш код, я получаю почти правильное поведение, за исключением того, что касания часто не распознаются на самом краю подслоя. Я немного запутался в том, что происходит. Возможно, вы захотите попробовать сделать что-то вроде того, когда вы регистрируете касание, бросаете новый крошечный слой вверх в том месте, где произошло касание (например, квадрат 5x5 какого-либо цвета), а затем снова удалите его через 3 секунды. Таким образом, вы можете отслеживать, где, по мнению CA, произошло касание, по сравнению с тем, где на самом деле находится ваш курсор.

Кстати, вам не нужно создавать CGImage для вашего синего содержимого, вы можете просто установить backgroundColor слоя на [UIColor blueColor].CGColor.

person Lily Ballard    schedule 05.01.2009