Làm cách nào để tôi có thể buộc dừng cuộn theo chương trình trong UIScrollView?


80

Lưu ý: Câu trả lời được đưa ra ở đây không phù hợp với tôi.

Tôi có UIScrollView (không phải chế độ xem bảng, chỉ là một thứ tùy chỉnh) và khi người dùng thực hiện các hành động nhất định, tôi muốn loại bỏ mọi thao tác cuộn (kéo hoặc giảm tốc) bên trong chế độ xem. Tôi đã thử làm ví dụ:

[scrollView scrollRectToVisible:CGRectInset([scrollView bounds], 10, 10) animated:NO];

về lý thuyết rằng, với một hình chữ nhật đã được biết trước có thể nhìn thấy được, việc cuộn sẽ chỉ dừng lại ở vị trí của nó, nhưng hóa ra điều này không có bất kỳ ảnh hưởng nào - rõ ràng là chế độ xem cuộn cho thấy rằng hình chữ nhật đã cho nằm trong giới hạn và mất không có hành động. Tôi có thể dừng cuộn, nếu tôi đưa ra một hình chữ nhật chắc chắn nằm ngoài giới hạn hiển thị nhưng bên trong contentSize của chế độ xem. Điều này dường như ngăn chế độ xem như mong đợi ... nhưng cũng khiến nó chuyển sang một số vị trí khác. Tôi có lẽ có thể làm một chút chơi xung quanh lề để làm cho điều này hoạt động hợp lý OK, nhưng có ai biết cách dễ dàng để ngăn chế độ xem cuộn đang hoạt động không?

Cảm ơn.

Câu trả lời:


108

Tôi đã chơi với giải pháp ban đầu của bạn một chút, và điều này dường như hoạt động tốt. Tôi nghĩ bạn gần như đã có nó, nhưng bạn chỉ đang bù đắp trực tràng mà bạn đã sử dụng quá nhiều, và quên rằng bạn chỉ có thể cuộn trực tràng thẳng trở lại trực tràng ban đầu.

Giải pháp tổng quát cho bất kỳ hành động cuộn nào là:

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    offset.x -= 1.0;
    offset.y -= 1.0;
    [scrollView setContentOffset:offset animated:NO];
    offset.x += 1.0;
    offset.y += 1.0;
    [scrollView setContentOffset:offset animated:NO];
}

[Chỉnh sửa] Kể từ iOS 4.3 (và có thể sớm hơn), tính năng này cũng hoạt động

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    [scrollView setContentOffset:offset animated:NO];
}

1
David: tuyệt vời, cảm ơn. Tôi đã xoay mình theo hướng này, nhưng cuối cùng đã phải tính toán ít tầm thường hơn về việc chọn một hình chữ nhật 1x1 ngay bên ngoài giới hạn cuộn. Thực hiện đề xuất của bạn về việc bù trừ và sau đó khôi phục ngay lập tức (điều này có vẻ thẳng thắn để tận dụng một hành vi chưa được xuất bản trong đó các lệnh gọi liên tiếp trong một lần chạy sự kiện thực sự hoạt động mặc dù "kết quả" phải là no-op), nó hoạt động tốt. Tôi sẽ chỉnh sửa câu trả lời của bạn ở trên để bao gồm giải pháp tổng quát sẽ hoạt động cho bất kỳ hướng cuộn nào. Cảm ơn!
Ben Zotto

(Hy vọng bạn không bận tâm đến các chỉnh sửa, muốn làm rõ cho những người đến sau. Cảm ơn câu trả lời của bạn!)
Ben Zotto

trông tuyệt vời ... nhân tiện, làm cách nào để bạn quay lại vị trí cũ? bạn có lưu phần bù trước khi giết nó, và sau đó quay lại đó?
Lior Frenkel

2
Trong iOS 7, khi scrollView isDecelerating, chỉ giải pháp hàng đầu hoạt động (giải pháp + = 1, - = 1).
Sahil

1
Giải pháp này có thể được cải thiện. Sử dụng + = 0,1 thay vì + = 1, vì vậy chỉ cần một lệnh gọi setContentOffsetlà đủ. Chế độ xem cuộn sẽ tự động làm tròn nội dung bù đắp.
kelin

91

Câu trả lời chung chung là, đó [scrollView setContentOffset:offset animated:NO]không giống như [scrollView setContentOffset:offset]!

  • [scrollView setContentOffset:offset animated:NO] thực sự dừng mọi hoạt ảnh đang chạy.
  • [scrollView setContentOffset:offset] không dừng bất kỳ hoạt ảnh đang chạy nào.
  • Tương tự cho scrollView.contentOffset = offset: không dừng bất kỳ hoạt ảnh đang chạy nào.

Điều đó không được ghi lại ở bất cứ đâu, nhưng đó là hành vi đã được thử nghiệm trên iOS 6.1 và iOS 7.1 - có lẽ cũng đã từng xảy ra trước đó.

Vì vậy, giải pháp để dừng hoạt ảnh / giảm tốc đang chạy rất đơn giản như sau:

[scrollView setContentOffset:scrollView.contentOffset animated:NO];

Về cơ bản những gì David Liu đã nói trong câu trả lời đã chỉnh sửa của mình. Nhưng tôi muốn nói rõ rằng hai API này KHÔNG giống nhau.

Swift 3:

scrollView.setContentOffset(scrollView.contentOffset, animated:false)

11
Cũng được thực hiện trên giải thích cho "người tìm lý do" giữa chúng ta ... (:
Aviel Gross

3
Điều này làm tôi buồn.
DCMaxxx

2
Tôi không hiểu tại sao công cụ này không được ghi nhận ... urggggghh
user1105951

Điều thú vị hơn nữa trong (ít nhất) iOS 11.3 và 11.4 là: Nếu contentOffset được làm động thành (0, 0)! Đây hẳn là một lỗi!
Ngày

68

Đối với tôi, câu trả lời được chấp nhận của David Lui ở trên không phù hợp với tôi. Đây là những gì tôi đã kết thúc:

- (void)killScroll {
    self.scrollView.scrollEnabled = NO;
    self.scrollView.scrollEnabled = YES;
}

Đối với những gì nó đáng giá, tôi đang sử dụng Trình mô phỏng iPhone iOS 6.0.


2
+1 như bạn đã nói, +1 cũng không hiệu quả với tôi, nhưng giải pháp này hoạt động tuyệt vời!
brenjt

1
Tuyệt vời, nó hoạt động hoàn hảo supportedInterfaceOrientationsđể ngăn các sự kiện cuộn tiếp tục nếu người dùng đang xoay thiết bị trong khi cuộn , điều này đôi khi gây lộn xộn tùy thuộc vào những gì bạn đang làm.
cprcrack

21

Đây là những gì tôi làm cho các chế độ xem cuộn của mình và tất cả các lớp con có liên quan khác:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
 {
    *targetContentOffset = scrollView.contentOffset;
 }

Điều này đặt giá targetContentOffsettrị scrollViewbù đắp hiện tại của ', do đó làm cho việc cuộn dừng lại vì nó đã đạt đến mục tiêu. Thực sự có ý nghĩa khi sử dụng một phương pháp có mục đích là người dùng có thể đặt mục tiêu contentOffset.

Nhanh

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
}

điều này có vẻ giống như một trong những nhà phát triển táo phải sử dụng. bởi vì tất cả những thứ khác đều dẫn đến lỗi tại một số điểm. tôi ngạc nhiên đây không phải là câu trả lời được chấp nhận.
OlDor

Đây là những gì phương pháp đại biểu này cho. câu trả lời chính xác.
Ryan

Theo tôi đây là giải pháp thanh lịch nhất như Apple đã nói trong tài liệu của mình: "Ứng dụng của bạn có thể thay đổi giá trị của tham số targetContentOffset để điều chỉnh nơi chế độ xem hoàn thành hoạt ảnh cuộn của nó". Làm việc rất tốt cho tôi
Macistador

đây là câu trả lời đúng. Không có cách nào ở trên phù hợp với tôi
sethu

20

Dừng cuộn nhanh chóng :

scrollView.setContentOffset(scrollView.contentOffset, animated: false)

16

Trên thực tế ... Cách "hiện đại" nhất sẽ là ->

scrollview.panGestureRecognizer.enabled = false;
scrollview.panGestureRecognizer.enabled = true;

Thao tác này sẽ vô hiệu hóa trình nhận dạng cử chỉ chịu trách nhiệm cuộn chỉ trong giây lát, thao tác này sẽ giết chết thao tác chạm hiện tại. Người dùng sẽ cần nhấc ngón tay lên và đặt ngón tay xuống để bắt đầu cuộn lại.

Chỉnh sửa: Điều này thực sự chỉ giết hoạt động kéo hiện tại của người dùng nhưng không dừng ngay việc giảm tốc nếu chế độ xem cuộn hiện ở trạng thái này. Để làm điều này, chỉnh sửa câu trả lời được chấp nhận là cách tốt nhất xD

[scrollview setContentOffset: scrollview.contentOffset animated:false];

Tôi sử dụng điều này trong một ứng dụng của tôi và nó thực hiện chính xác những gì OP yêu cầu. Bạn có thể vui lòng cho biết thêm một chút bối cảnh được không? Điều gì không hoạt động hoặc tại sao?
Xatian

Ồ, xin lỗi ... Tôi nghĩ là tôi biết ... Tôi chỉ trả lời phần câu hỏi mà tôi đang tìm kiếm ... phần thứ hai tôi đã bỏ qua ... :-) -> đã chỉnh sửa.
Xatian

Không ngừng giảm tốc.
Rudolf Adamkovič

5

Cách rõ ràng nhất là phân lớp UIScrollView và cung cấp phương thức setContentOffset của riêng bạn. Điều này sẽ chuyển thông báo, chỉ khi bạn chưa bật thuộc freezetính boolean của mình .

Như vậy:

BOOL freeze; // and the @property, @synthesize lines..

-(void)setContentOffset:(CGPoint)offset
{
    if ( !freeze ) [super setContentOffset:offset];
}

Sau đó, để đóng băng:

scrollView.freeze = YES;

1
Cảm ơn. Trong khi điều này tạm dừng quá trình cuộn có thể nhìn thấy, nó không thực sự dừng quá trình giảm tốc bên trong. Nếu bạn bật tính năng này và sau đó tắt lại sau một lúc, bạn sẽ thấy tạm dừng cuộn, sau đó chuyển tiếp và tiếp tục giảm tốc. Vì vậy, trạng thái bên trong của scrollview vẫn là cuộn. Vì vậy, bạn cần phải đóng băng cho đến khi bạn biết quá trình giảm tốc đã dừng lại, điều này bạn có thể làm khi hợp tác với một người được ủy quyền, nhưng tôi cảm thấy hơi ngốc nghếch. Tôi hy vọng điều gì đó thực tế chỉ làm giảm tốc độ giảm tốc ngay lập tức (ví dụ: có tác dụng của một lần chạm vào màn hình).
Ben Zotto

ok, những vấn đề này nghe có vẻ quen thuộc từ trải nghiệm trước đây ... tại sao không giảm tốc độ bằng cách thiết lập decelerationRate = 1e10;trong khi đóng băng == YES?
mvds

(không biết toán nội bộ, 10 * UIScrollViewDecelerationRateFast có thể là một lựa chọn khôn ngoan hơn 1e10)
mvds

Ý tưởng thú vị! Thật không may, giá trị này dường như bị kẹp chặt. Đã thử và dường như không có giá trị nào của điều này sẽ khiến quá trình giảm tốc dừng ngay lập tức. Tốt nhất tôi có thể làm là làm cho nó chậm chạp. :)
Ben Zotto

kế hoạch B sau đó là vô hiệu hóa tính năng cuộn trong một khoảng thời gian ngắn - có thể bạn có thể bỏ qua nó, vì dù sao thì bạn cũng đang chặn setContentOffset, hy vọng ngăn chặn các tác dụng phụ khi tắt tính năng cuộn.
mvds


3

Tắt chỉ cuộn tương tác của người dùng. (nhanh)

scrollView.isScrollEnabled = false

Tắt trong khi cuộn hoạt ảnh sau khi kéo. (nhanh)

var scrollingByVelocity = false

func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
    if !scrollingByVelocity {
        scrollView.setContentOffset(scrollView.contentOffset, animated: false)
    }
}

0

Tôi đã thử các phương pháp này trong collectionview:

self.collectionView.collectionViewLayout.finalizeCollectionViewUpdates()


0

Điều này phù hợp với tôi trong Swift 4.2 :

   func killScroll() {
    self.scrollView.isScrollEnabled = false;
    self.scrollView.isScrollEnabled = true;
}

... như một phần mở rộng:

extension UIScrollView {
    func killScroll() {
        self.isScrollEnabled = false;
        self.isScrollEnabled = true;

    }
}

0

Tôi chỉ muốn tắt tính năng cuộn khi một UIView nhất định trong chế độ xem cuộn là nguồn của cảm ứng trong khi vuốt. Sẽ cần khá nhiều cấu trúc lại để di chuyển UIView ra bên ngoài UIScrollView, vì chúng tôi có một hệ thống phân cấp chế độ xem phức tạp.

Để giải quyết vấn đề này, tôi đã thêm một UIPanGestureRecognizer duy nhất vào chế độ xem phụ mà tôi muốn ngăn không cho cuộn. UIPanGestureRecognizer này sẽcancelsTouchesInView ngăn panGesture của UIScrollView kích hoạt.

Đó là một chút 'hack', nhưng đó là một thay đổi siêu dễ dàng và nếu bạn đang sử dụng XIB hoặc Storyboard, tất cả những gì bạn cần làm là kéo cử chỉ xoay vào chế độ xem phụ được đề cập.

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.