Базовое перетаскивание в iOS


93

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


Ответы:


126

Предположим, у вас есть UIViewсцена с фоновым изображением и многие другие vehicles, вы можете определить каждое новое транспортное средство как UIButton(UIImageView, вероятно, тоже будет работать):

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(imageTouch:withEvent:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:@selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button setImage:[UIImage imageNamed:@"vehicle.png"] forState:UIControlStateNormal];
[self.view addSubview:button];

Затем вы можете переместить автомобиль куда хотите, отреагировав на UIControlEventTouchDragInsideсобытие, например:

- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
    CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
    UIControl *control = sender;
    control.center = point;
}

Отдельному транспортному средству намного проще справляться с собственным перетаскиванием, чем управлять сценой в целом.


Выглядит здорово и очень просто! Я попробую это сделать.
Люк

10
Будет ли эта стратегия вызывать прерывание перетаскивания, если пользователь перетаскивает кнопку быстрее, чем обновляется анимация? Если бы их палец вышел за пределы коробки, он перестал бы получать вызовы imageMoved: withEvent:, верно?
Тим

Как узнать, перестало ли перетаскивать изображение? Откуда вы знаете, что палец съехал?
ymutlu

оххо - можно ли сместить "ручку" перетаскивания изображения или кнопки так, чтобы ее можно было перетаскивать за верхний правый или верхний левый угол?
LJ Wilson

когда я пытаюсь получить исключение - [drag_move_textViewController imageTouch: withEvent:]: нераспознанный селектор отправлен в экземпляр 0x4b4b1c0
iosDev

34

В дополнение к ответу ohho я пробовал аналогичную реализацию, но без проблем с «быстрым» перетаскиванием и центрированием для перетаскивания.

Инициализация кнопки ...

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button addTarget:self action:@selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragOutside];
[button setImage:[UIImage imageNamed:@"vehicle.png"] forState:UIControlStateNormal];
[self.view addSubview:button];

и реализация метода:

- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
    UIControl *control = sender;

    UITouch *t = [[event allTouches] anyObject];
    CGPoint pPrev = [t previousLocationInView:control];
    CGPoint p = [t locationInView:control];

    CGPoint center = control.center;
    center.x += p.x - pPrev.x;
    center.y += p.y - pPrev.y;
    control.center = center;
}

Я не говорю, что это идеальное решение для ответа. Тем не менее я нашел это самым простым решением для перетаскивания.


2
В вашем примере кода вы определяете UIControl * control = sender после его использования - не следует ли это определять ранее?
Питер Джонсон

@PeterJohnson: На самом деле в данном случае это не имеет значения. Эта переменная не использовалась ранее, начиная с упомянутой вами строки :)
JakubKnejzlik

1
А как насчет CGPoint pPrev = [t previousLocationInView: control]; CGPoint p = [t locationInView: control]; Эти две предыдущие строки, кажется, относятся к «контролю»?
Питер Джонсон

О Боже! Как я мог это пропустить? : -O ... Я отредактировал ответ.
JakubKnejzlik

Я добавляю конструктор интерфейса формы кнопки и использую автолайп для некоторого входа в систему. Я скрываю кнопку и показываю ее снова, когда я это сделаю, чтобы она вернулась в исходное место, как предотвратить это поведение
Amr Angry

2

У меня был случай, когда я хотел перетаскивать uiviews между несколькими другими uiviews. В этом случае я нашел лучшее решение для отслеживания событий панорамирования в суперпредставлении, содержащем все зоны перетаскивания и все объекты перетаскивания. Основная причина, по которой НЕ добавлялись слушатели событий к фактическим перетаскиваемым представлениям, заключалась в том, что я потерял текущие события панорамирования, как только я изменил супервизор для перетаскиваемого представления.

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

Я создал простой пример, который можно увидеть здесь: Перетащите перетаскивание uiviews между несколькими другими uiviews

Если вы решите пойти другим путем, попробуйте использовать uicontrol вместо uibutton. Они объявляют методы addTarget и их намного проще расширять - следовательно, они дают настраиваемое состояние и поведение.


1

На самом деле я бы отслеживал перетаскивание самого вида транспортного средства , а не большого вида - если нет особой причины «нет».

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

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


1
-(void)funcAddGesture
{ 

 // DRAG BUTTON
        UIPanGestureRecognizer *panGestureRecognizer;
        panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragButton:)];
        panGestureRecognizer.cancelsTouchesInView = YES;

        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(50, 100, 60, 60);
        [button addTarget:self action:@selector(buttontapEvent) forControlEvents:UIControlEventPrimaryActionTriggered];
        [button setImage:[UIImage imageNamed:@"button_normal.png"] forState:UIControlStateNormal];
        [button addGestureRecognizer:panGestureRecognizer];
        [self.view addSubview:button];
}


- (void)dragButton:(UIPanGestureRecognizer *)recognizer {

    UIButton *button = (UIButton *)recognizer.view;
    CGPoint translation = [recognizer translationInView:button];

    float xPosition = button.center.x;
    float yPosition = button.center.y;
    float buttonCenter = button.frame.size.height/2;

    if (xPosition < buttonCenter)
        xPosition = buttonCenter;
    else if (xPosition > self.view.frame.size.width - buttonCenter)
        xPosition = self.view.frame.size.width - buttonCenter;

    if (yPosition < buttonCenter)
        yPosition = buttonCenter;
    else if (yPosition > self.view.frame.size.height - buttonCenter)
        yPosition = self.view.frame.size.height - buttonCenter;

    button.center = CGPointMake(xPosition + translation.x, yPosition + translation.y);
    [recognizer setTranslation:CGPointZero inView:button];
}


- (void)buttontapEvent {

    NSLog(@"buttontapEvent");
}

1

Ответ оххо сработал для меня. Если вам нужна версия swift 2.x:

override func viewDidLoad() {

    let myButton = UIButton(type: .Custom)
    myButton.setImage(UIImage(named: "vehicle"), forState: .Normal)

    // drag support
    myButton.addTarget(self, action:#selector(imageMoved(_:event:)), forControlEvents:.TouchDragInside)
    myButton.addTarget(self, action:#selector(imageMoved(_:event:)), forControlEvents:.TouchDragOutside)
}

private func imageMoved(sender: AnyObject, event: UIEvent) {
    guard let control = sender as? UIControl else { return }
    guard let touches = event.allTouches() else { return }
    guard let touch = touches.first else { return }

    let prev = touch.previousLocationInView(control)
    let p = touch.locationInView(control)
    var center = control.center
    center.x += p.x - prev.x
    center.y += p.y - prev.y
    control.center = center
}


0

Для Swift 4 Простой и легкий способ. 😛

Шаг 1. Подключите кнопку из раскадровки к контроллеру просмотра. И установите все основные ограничения AutoLayout

 @IBOutlet weak var cameraButton: UIButton!

Шаг 2. Добавьте жест панорамирования для кнопки In viewDidLoad ()

self.cameraButton.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGestureHandler(panGesture:))))

Шаг 3:

Добавить panGestureHandler

@objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {

         let location = recognizer.location(in: view)
        cameraButton.center = location

    }

А теперь действие вашей кнопки

@IBAction func ButtonAction(_ sender: Any) {

        print("This is button Action")
    }

введите описание изображения здесь

Добавить Посмотреть результат ☝️

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.