UIScrollview с UIButtons - как воссоздать трамплин?

Я пытаюсь создать в своем приложении интерфейс, похожий на трамплин. Я пытаюсь использовать UIButtons, добавленные в UIScrollView. Проблема, с которой я сталкиваюсь, заключается в том, что кнопки не передают никаких прикосновений к UIScrollView - если я попытаюсь щелкнуть / сдвинуть и нажать на кнопку, она не зарегистрируется для UIScrollView, но если я щелкну пространство между кнопки все будет работать. Кнопки щелкают / работают, если я их касаюсь.

Есть ли свойство или параметр, который заставляет кнопку отправлять события касания своему родительскому элементу (супервизору)? Нужно ли добавлять кнопки к чему-то еще перед добавлением UIScrollView?

Вот мой код:

//init scrolling area
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 480, 480)];
scrollView.contentSize = CGSizeMake(480, 1000);
scrollView.bounces = NO;
scrollView.delaysContentTouches = NO;

//create background image
UIImageView *rowsBackground = [[UIImageView alloc] initWithImage:[self scaleAndRotateImage:[UIImage imageNamed:@"mylongbackground.png"]]];
rowsBackground.userInteractionEnabled = YES;

//create button
UIButton *btn = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
btn.frame = CGRectMake(100, 850, 150, 150);
btn.bounds = CGRectMake(0, 0, 150.0, 150.0);
[btn setImage:[self scaleAndRotateImage:[UIImage imageNamed:@"basicbutton.png"]] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];

//add "stuff" to scrolling area
[scrollView addSubview:rowsBackground];
[scrollView addSubview:btn];

//add scrolling area to cocos2d
//this is just a UIWindow
[[[Director sharedDirector] openGLView] addSubview:scrollView];

//mem-mgmt
[rowsBackground release];
[btn release];
[scrollView release];

person Patrick    schedule 16.03.2009    source источник
comment
Следует отметить, что, хотя Apple разрешила это раньше, 10.4 Apps that create alternate desktop/home screen environments or simulate multi-App widget experiences will be rejected. На всякий случай некоторые новички планировали выпустить производственное приложение, у которого есть собственный трамплин.   -  person user    schedule 19.12.2013


Ответы (7)


Чтобы UIScrollView мог определить разницу между щелчком, который переходит в его представление (я) содержимого, и касанием, которое превращается в смахивание или щипок, ему необходимо отложить касание и посмотреть, переместился ли ваш палец во время этой задержки. Установив delaysContentTouches на NO в приведенном выше примере, вы предотвратите это. Следовательно, представление прокрутки всегда передает прикосновение к кнопке, а не отменяет его, когда выясняется, что пользователь выполняет жест смахивания. Попробуйте установить delaysContentTouches на YES.

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

person Brad Larson    schedule 18.03.2009
comment
Одна из возможных проблем с выполнением delaysContentTouches заключается в том, что добавляется задержка перед выделением кнопки. Ответ Романа К. ниже позволит вам не иметь задержки, но по-прежнему иметь возможность прокручивать после нажатия кнопки. - person GhostM; 04.07.2011
comment
Совет по структуре отличный, но мне было бы интересно узнать, почему. Спасибо. - person Lorenzo B; 04.06.2014

Решение, которое сработало для меня, включало:

  1. Установка canCancelContentTouches в UIScrollView на YES.
  2. Расширение UIScrollView для переопределения touchesShouldCancelInContentView:(UIView *)view для возврата YES, когда view равно UIButton.

Согласно документации touchesShouldCancelInContentView возвращает «YES для отмены дальнейших сенсорных сообщений для просмотра, NO для продолжения просмотра для получения этих сообщений. Возвращаемое значение по умолчанию - YES, если представление не является UIControl объектом; в противном случае оно возвращает NO».

Поскольку UIButton - это UIControl, расширение необходимо, чтобы canCancelContentTouches вступил в силу, позволяющую прокрутку.

person Roman Kishchenko    schedule 04.08.2010
comment
Большое тебе спасибо! Чтобы уточнить, это лучше всего работает, когда delaysContentTouches = NO. - person Schrockwell; 06.07.2011
comment
Это путь! (по крайней мере, для iOS 4+) Я обнаружил, что наиболее универсальным вариантом является переопределение следующим образом: - (BOOL)touchesShouldCancelInContentView:(UIView *)view { return ![view isKindOfClass:[UISlider class]]; } - person Neal Ehardt; 07.08.2011
comment
У меня все еще не работает. touchesShouldCancelInContentView никогда не вызывается, если я не установил для задержки значение NO, но в последнем случае прокрутки нет вообще. - person Michael Chourdakis; 02.12.2013
comment
@Michael: да, конечно, это не работает, автор не удосужился прочитать документацию BOOL delaysContentTouches; // default is YES. if NO, we immediately call -touchesShouldBegin:withEvent:inContentView:, и многие проголосовавшие не имеют ни малейшего представления о том, что она делает. - person Ben Affleck; 03.05.2014

У меня есть аналогичный случай, когда несколько кнопок в UIScrollView, и я хочу прокрутить эти кнопки. Вначале я разделил UIScrollView и UIButton на подклассы. Однако я заметил, что мой подкласс UIScrollView не получил событие touchesEnded, поэтому я перешел только на подкласс UIButton.


@interface MyPhotoButton : UIButton {
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
@end

@implementation MyPhotoButton

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    [self setEnabled:NO];
    [self setSelected:NO];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    [self setEnabled:YES];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    [self setEnabled:YES];
}

@end
person Richard Chen    schedule 27.04.2010

UIScrollView сам обрабатывает множество событий. Вам нужно вручную обработать touchDidEnd и проверку нажатия для кнопок внутри UIScrollView.

person Rog    schedule 16.03.2009

Хорошо, вот ваш ответ:

Подкласс UIButton. (ПРИМЕЧАНИЕ: вызывайте [super ....] в начале каждого переопределения.

  • Добавить недвижимость. Один из типа BOOL (называется enableToRestore)
  • Добавить недвижимость. Один из типа CGPoint (так называемый startTouchPosition)
  • в awakeFromNib и initWithFrame установите enableToRestore в свойство isEnabled)
  • Переопределите "touchesBegan: withEvent:", чтобы сохранить начало позиции касания.
  • Переопределите "touchesMoved: withEvent:", чтобы проверить, есть ли горизонтальное перемещение.
  • Если ДА, установите для параметра «Включено» значение «НЕТ» и выберите значение «НЕТ».

Образец кода:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    UITouch *touch = [touches anyObject];

    [super touchesBegan:touches withEvent:event];
    [self setStartTouchPosition:[touch locationInView:self]];
}


//
// Helper Function
//
- (BOOL)isTouchMovingHorizontally:(UITouch *)touch 
{
    CGPoint currentTouchPosition = [touch locationInView:self];
    BOOL      rValue = NO;

    if (fabsf([self startTouchPosition].x - currentTouchPosition.x) >= 2.0) 
    {
        rValue = YES;
    }

    return (rValue);
}

//
// This is called when the finger is moved.  If the result is a left or right
// movement, the button will disable resulting in the UIScrollView being the
// next responder.  The parrent ScrollView will then re-enable the button
// when the finger event is ended of cancelled.
//
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{
    [super touchesMoved:touches withEvent:event];
    if ([self isTouchMovingHorizontally:[touches anyObject]]) 
    {
        [self setEnabled:NO];
        [self setSelected:NO];
    } 
}

Это активирует UIScrollView.

Подкласс UIScrollView. (ПРИМЕЧАНИЕ: вызывайте [super ....] в начале каждого переопределения.

  • Переопределите как "touchesEnded: withEvent:", так и "touchesCancelled: withEvent:"
  • В переопределении сбросьте все подпредставления (и их подпредставления) включенным флагом.
  • ПРИМЕЧАНИЕ. Используйте категорию и добавьте метод в UIView:

.

- (void) restoreAllEnables
{
    NSArray   *views = [self subviews];

    for (UIView *aView in views)
    {
        if ([aView respondsToSelector:@selector(restoreEnable)])
        {
            [aView restoreEnable];
        }
    }
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self restoreAllEnables];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self restoreAllEnables];
}
  • В категории:

.

-(void) restoreEnable
{
    NSArray   *views = [self subviews];

    if ([self respondsToSelector:@selector(enableToRestore)])
    {
        [self setEnabled:[self enableToRestore]];
    }

    for (UIView *aView in views)
    {
        if ([aView respondsToSelector:@selector(restoreEnable)])
        {
            [aView restoreEnable];
        }
    }
}

РЕДАКТИРОВАТЬ Примечание: у меня никогда не было ответа 3 для работы. Аналогично: setDelaysContentTouches: NO (установленный в контроллере представления или где-то еще) должен быть установлен для достижения наилучших результатов в ответе 4. Это обеспечивает очень быстрый отклик на кнопки. Установка setDelaysContentTouches: YES серьезно влияет (150 мс) на время отклика на кнопки и делает невозможным легкое и быстрое касание.

person Steven Noyes    schedule 26.02.2010

Другой способ:
1. Замените кнопку простым пользовательским UIView
2. Ставим флаг «userInterationEnable = yes;» в методе инициализации
3. В представлении переопределения метода UIResponder "touchesEnded" здесь вы можете инициировать необходимое действие, например, кнопку.

person user41806    schedule 30.05.2009

По моему опыту, первый ответ, то есть простая установка delaysContentTouches на YES, ничего не меняет в отношении проблемы. Кнопки по-прежнему не доставляют результаты отслеживания в режим прокрутки. Третий ответ одновременно прост и очень удобен. Спасибо sieroaoj!

Однако для того, чтобы третий ответ работал, вам также необходимо установить delaysContentTouches в YES. В противном случае метод touchesEnded также будет вызываться для отслеживания в представлении. Поэтому я мог решить проблему следующим образом:

  1. Замените кнопку простым пользовательским UIView
  2. Ставим флаг «userInterationEnable = yes;» по методу инициализации
  3. В представлении переопределения метода UIResponder "touchesEnded" здесь вы можете запустить действие, которое вы

Четвертое. установите delaysContentTouches на YES

person Captain Fim    schedule 11.11.2009
comment
delaysContentTouches = YES по умолчанию. - person Ben Affleck; 03.05.2014