Kéo và thả cơ bản trong iOS


93

Tôi muốn xem trong đó có những phương tiện đang chạy xung quanh mà người dùng cũng có thể kéo và thả. Bạn nghĩ đâu là chiến lược quy mô lớn tốt nhất để thực hiện điều này? Tốt nhất là nhận các sự kiện liên lạc từ chế độ xem đại diện cho các phương tiện hay từ chế độ xem lớn hơn? Có mô hình đơn giản nào bạn đã sử dụng để kéo và thả mà bạn hài lòng không? Hạn chế của các chiến lược khác nhau là gì?


Câu trả lời:


126

Giả sử bạn có một UIViewcảnh với một hình nền và nhiều hình nền vehicles, bạn có thể xác định mỗi phương tiện mới là một UIButton(UIImageView cũng có thể hoạt động):

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];

Sau đó, bạn có thể di chuyển chiếc xe đến bất cứ nơi nào bạn muốn, bằng cách phản hồi UIControlEventTouchDragInsidesự kiện, ví dụ:

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

Phương tiện cá nhân dễ dàng hơn rất nhiều để xử lý các lực kéo của chính nó, so với việc quản lý toàn bộ hiện trường.


Trông tuyệt vời và rất đơn giản! Tôi sẽ thử cái này.
Luke

10
Chiến lược này có khiến kéo bị hỏng nếu người dùng kéo nút nhanh hơn hoạt ảnh được cập nhật không? Nếu ngón tay của họ lọt ra ngoài hộp, nó sẽ ngừng nhận cuộc gọi imageMished: withEvent:, đúng không?
Tim

Làm thế nào bạn có thể biết nếu hình ảnh ngừng kéo? Làm thế nào bạn biết ngón tay di chuyển ra ngoài?
ymutlu

ohho - có thể bù lại "tay cầm" kéo cho một hình ảnh hoặc nút để nó có thể được kéo theo góc trên bên phải hoặc phía trên bên trái không?
LJ Wilson

khi tôi đang cố gắng nhận được ngoại lệ - [drag_move_textViewController imageTouch: withEvent:]: chọn không được công nhận gửi đến dụ 0x4b4b1c0
iosDev

34

Ngoài câu trả lời của ohho, tôi đã thử triển khai tương tự, nhưng không gặp vấn đề kéo và căn giữa để kéo "nhanh".

Khởi tạo nút là ...

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];

và thực hiện phương pháp:

- (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;
}

Tôi không nói, rằng đây là giải pháp hoàn hảo cho câu trả lời. Tuy nhiên, tôi thấy đây là giải pháp dễ nhất để kéo.


2
Trong mã ví dụ của bạn, bạn xác định UIControl * control = sender sau khi nó được sử dụng- điều đó không nên được định nghĩa trước đó?
Peter Johnson

@PeterJohnson: Thực tế thì nó không quan trọng trong trường hợp này. Không có sử dụng trước đó của biến này kể từ khi dòng Bạn đã đề cập :)
JakubKnejzlik

1
Còn về CGPoint pPrev = [t beforeLocationInView: control]; CGPoint p = [t locationInView: control]; Hai dòng trước đó đều xuất hiện để chỉ "kiểm soát"?
Peter Johnson

Ôi trời! Làm thế nào tôi có thể bỏ lỡ điều đó? : -O ... Tôi đã sửa câu trả lời.
JakubKnejzlik

i thêm người xây dựng nút giao diện hình thức và tôi sử dụng autolayout đối với một số đăng nhập tôi làm ẩn nút và hiển thị nó một lần nữa khi tôi làm như vậy nó trở về vị trí ban đầu như thế nào để ngăn chặn beviour này
Amr Angry

2

Tôi gặp trường hợp muốn kéo / thả uiview giữa nhiều uiview khác. Trong trường hợp đó, tôi đã tìm ra giải pháp tốt nhất để theo dõi các sự kiện xoay trong một chế độ xem siêu có chứa tất cả các vùng thả và tất cả các đối tượng đầu hồi kéo. Lý do chính để KHÔNG thêm trình xử lý sự kiện vào chế độ xem được kéo thực tế là tôi đã mất các sự kiện xoay đang diễn ra ngay sau khi tôi thay đổi chế độ xem siêu cấp cho chế độ xem được kéo.

Thay vào đó, tôi đã triển khai một giải pháp kéo thả khá chung chung có thể được sử dụng trên một hoặc nhiều vùng thả.

Tôi đã tạo một ví dụ đơn giản có thể xem ở đây: Kéo một uiview thả giữa nhiều uiview khác

Nếu bạn quyết định đi theo hướng khác, hãy thử sử dụng uicontrol thay vì uibutton. Chúng khai báo các phương thức addTarget và dễ mở rộng hơn nhiều - do đó cung cấp trạng thái và hành vi tùy chỉnh.


1

Tôi thực sự sẽ theo dõi lực kéo trên chính chế độ xem xe , thay vì chế độ xem lớn - trừ khi có một lý do cụ thể nào đó.

Trong một trường hợp, tôi cho phép người dùng đặt các mục bằng cách kéo chúng trên màn hình. Trong trường hợp đó, tôi đã thử nghiệm với cả chế độ xem trên cùngchế độ xem con có thể kéo được. Tôi thấy đó là mã sạch hơn nếu bạn thêm một vài chế độ xem "có thể kéo" vào UIView và xử lý cách chúng có thể được kéo. Tôi đã sử dụng một lệnh gọi lại đơn giản đến UIView chính để kiểm tra xem vị trí mới có phù hợp hay không - vì vậy tôi có thể chỉ ra bằng hình ảnh động.

nhìn từ trên xuống theo dõi kéo tôi đoán là tốt, nhưng mà làm cho nó lộn xộn một chút nhiều hơn nếu bạn muốn thêm lượt xem không phải kéo mà vẫn tương tác với người sử dụng, chẳng hạn như một nút.


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

câu trả lời của ohho đã làm việc tốt cho tôi. Trong trường hợp bạn cần phiên bản 2.x nhanh chóng:

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

Đối với Swift 4 Cách đơn giản và dễ dàng nhất. 😛

Bước 1: Kết nối nút của bạn từ bảng phân cảnh để xem bộ điều khiển. Và đặt tất cả các ràng buộc cơ bản của tự động thanh toán

 @IBOutlet weak var cameraButton: UIButton!

Bước 2: Thêm Cử chỉ Pan cho nút của bạn Trong viewDidLoad ()

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

Bước 3:

Thêm panGestureHandler

@objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {

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

    }

Và bây giờ là hành động nút của bạn

@IBAction func ButtonAction(_ sender: Any) {

        print("This is button Action")
    }

nhập mô tả hình ảnh ở đây

Thêm Xem kết quả ☝️

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.