2011-01-05 7 views
13

Tôi có một UISlider là một phần của chế độ xem được tải vào một UIScrollView khi bật phân trang. Tôi đã nhận thấy một hành vi bất ngờ. Nếu người dùng cố sử dụng thanh trượt một cách nhanh chóng (tức là nhấn và di chuyển), nó sẽ "kích hoạt" chế độ xem cuộn, làm cho trang chuyển đổi. Tuy nhiên, nếu nhấn và giữ thanh trượt thứ hai "kích hoạt" và sau đó bạn có thể điều chỉnh giá trị thanh trượt. Hành vi này là không mong muốn.UISlider và UIScrollView

Cách tốt nhất để làm cho đáp ứng UISlider khi được tải vào một UIScrollView là gì? Tôi đã nghĩ về việc thêm một "chặn" xem mà chỉ ăn lên các sự kiện cảm ứng được đặt dưới thanh trượt, nhưng không chắc chắn nếu đây là cách tốt nhất để đi về nó.

Trả lời

3

Chế độ xem cuộn của bạn phát hiện các chạm trên vùng bị chiếm bởi thanh trượt của bạn (ghi đè hitTest:withEvent:, bạn có thể cần phải phân lớp UIScrollView). Nếu phát hiện thấy một vùng chạm trên khu vực, hãy cho biết chế độ xem cuộn của bạn để ngay lập tức chuyển liên lạc sang thanh trượt.

+0

Cảm ơn, đã kết thúc với điều đó. Tuy nhiên, một sự kỳ quặc khác ... hành vi này dường như chỉ xuất hiện trên giả lập. Một khi tôi đã nhận nó trên thiết bị nó làm việc tốt mà không cần sửa chữa. – MarkPowell

+0

Thú vị, lý do khác không chỉ dựa vào giả lập để kiểm tra ... – BoltClock

+2

FWIW, tôi thấy chính xác hành vi tương tự (như được mô tả trong câu hỏi gốc) trên cả trình mô phỏng (iOS 6) và thiết bị (iOS 5.1). Tôi phải chạm và giữ một lúc trước khi kéo thanh trượt, nếu không tôi sẽ nhận được chế độ xem cuộn cuộn thay thế. –

40

không cần thử nghiệm lần truy cập ở bên UIScrollView. vì độ trễ được thiết lập bởi chính số UIScrollView. phân lớp và triển khai hitTest:withEvent: tùy chỉnh sẽ không giúp ích vì nó vẫn được kích hoạt với độ trễ.

tôi đã tìm kiếm giờ để có giải pháp thanh lịch cho điều này, vì tôi muốn mô phỏng thiết bị lưu trữ riêng của apple trong trình chuyển đổi ứng dụng ios.

lừa:

yourScrollView.delaysContentTouches = NO; 

tiếc là điều này sẽ vô hiệu hóa các sự kiện cùng các ca khúc UISliders, vì vậy đối với phần này bạn UIScrollView sẽ không kích hoạt bất kỳ touchevents vì họ đang đánh bắt bằng thanh trượt đầu tiên.

để vượt qua touchevents khác so với những người mà đang ở trong UISliders thumb rect bạn phải phân lớp UISlider và thêm dòng sau:

// get the location of the thumb 
- (CGRect)thumbRect 
{ 
    CGRect trackRect = [self trackRectForBounds:self.bounds]; 
    CGRect thumbRect = [self thumbRectForBounds:self.bounds 
            trackRect:trackRect 
             value:self.value]; 
    return thumbRect; 
} 

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGRect thumbFrame = [self thumbRect]; 

     // check if the point is within the thumb 
    if (CGRectContainsPoint(thumbFrame, point)) 
    { 
     // if so trigger the method of the super class 
     NSLog(@"inside thumb"); 
     return [super hitTest:point withEvent:event]; 
    } 
    else 
    { 
     // if not just pass the event on to your superview 
     NSLog(@"outside thumb"); 
     return [[self superview] hitTest:point withEvent:event]; 
    } 
} 
+0

Cảm ơn Sebastian, đó là chính xác những gì tôi đang tìm kiếm! –

+0

bạn được chào đón, alex. –

+0

Tôi đang tìm kiếm chính xác điều tương tự, cảm ơn! –

0

Bạn có thể phân lớp UIScrollView và ghi đè phương pháp - (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view như sau:

- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view { 
    if ([view isKindOfClass:[UISlider class]]) { 
     UITouch *touch = [[event allTouches] anyObject]; 
     CGPoint location = [touch locationInView:view]; 
     CGRect thumbRect; 
     UISlider *mySlide = (UISlider*) view; 
     CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds]; 
     thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value]; 
     if (CGRectContainsPoint(thumbRect, location)) 
      return YES; 
    } 
    return NO; 
} 

Đồng thời đặt thuộc tính yourScrollView.delaysContentTouches = NO; cho chế độ xem cuộn.

1

Trong khi tạo một máy nghe nhạc âm thanh tôi đã có vấn đề chính xác với một sliderView và nó được thêm vào một scrollView, và bất cứ khi nào tôi chạm vào sliderView, các scrollView sử dụng để có được các liên lạc và thay vì sliderView, các scrollView di chuyển. Để tránh điều này, tôi đã vô hiệu hóa srcolling của scrollView khi người dùng chạm vào sliderView và nếu không cuộn sẽ được bật.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 

    UIView *result = [super hitTest:point withEvent:event] ; 
    if (result == sliderView) 
    { 
     scrollView.scrollEnabled = NO ; 
    } 
    else 
    { 
     scrollView.scrollEnabled = YES ; 
    } 

    return result ; 

} 
1

Vấn đề của tôi là một superset của vấn đề này - Tôi đã có UISliders bên UITableViewCells và cả UITableView là một trang bên trong một UIScrollView. Các thanh trượt đã tàn phá các tương tác với hai loại kia và các giải pháp phân lớp không hoạt động. Dưới đây là những gì tôi đã đưa ra đó là làm việc tuyệt vời: gửi thông báo khi thanh trượt di chuyển và có UITableView và UIScrollView disableScrolling trên trong thời gian này. Lưu ý trong hình dưới đây: thanh trượt của tôi là ngang, tableview của tôi là dọc, và UIScrollView của tôi có các trang nằm ngang.

UISlider in a UITableView in a UIScrollView

Các UITableViewCell nhặt các sự kiện cho thanh trượt lập trình tạo:

self.numberSlider = [[UISlider alloc] init]; 
[self.numberSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchDown:) forControlEvents:UIControlEventTouchDown]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpOutside]; 

Theo mục đích của hướng dẫn này, chúng tôi chỉ quan tâm đến Touchdown và Up:

- (void)sliderTouchDown:(UISlider *)sender 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
} 

- (void)sliderTouchUp:(UISlider *)sender 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

Bây giờ, chúng tôi nắm bắt các thông báo này trong cả UITableView (lưu ý rằng tableview là trong VC nhưng tôi chắc chắn điều này sẽ làm việc nếu bạn subcl assed):

- (void)viewDidLoad 
{ 
    // other stuff 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchDown:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchUp:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

- (void)sliderTouchDown:(NSNotification *)notify 
{ 
    self.treatmentTableView.scrollEnabled = NO; 
} 

- (void)sliderTouchUp:(NSNotification *)notify 
{ 
    self.treatmentTableView.scrollEnabled = YES; 
} 

và UIScrollView (tương tự như trên, kèm theo trong một VC):

- (void)viewDidLoad 
{ 
    // other stuff 

    // Register for slider notifications 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disableScrolling:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enableScrolling:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

- (void)disableScrolling:(NSNotification *)notify 
{ 
    self.scrollView.scrollEnabled = NO; 
} 

- (void)enableScrolling:(NSNotification *)notify 
{ 
    self.scrollView.scrollEnabled = YES; 
} 

Tôi muốn nghe một giải pháp thanh lịch hơn, nhưng điều này chắc chắn được hoàn thành công việc . Khi bạn sử dụng một thanh trượt, bảng và các cuộn xem vẫn giữ nguyên và khi bạn nhấp vào bên ngoài thanh trượt, các xem bảng và cuộn sẽ di chuyển như mong đợi. Ngoài ra - lưu ý rằng tôi có thể sử dụng các phiên bản không thuộc lớp con của tất cả 3 thành phần trong giải pháp này. Hy vọng điều này sẽ giúp một ai đó!

1

Tôi muốn đăng bài này làm nhận xét, nhưng tài khoản của tôi không có đại diện nên tôi phải đăng toàn bộ câu trả lời. Câu trả lời của user762391 bao gồm ghi đè hitTest hoạt động hoàn hảo. Tôi chỉ muốn nói thêm rằng thay vì trọng hitTest, bạn thay vì có thể ghi đè lên pointInside: withEvent: như thế này (giữ phương pháp thumbRect):

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent * _Nullable)event { 
    return CGRectContainsPoint([self thumbRect], point) 
} 

hoặc trong Swift:

var thumbRect: CGRect { 
    return thumbRectForBounds(bounds, trackRect: trackRectForBounds(bounds), value: value) 
} 

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { 
    return thumbRect.contains(point) 
} 
1

upvoted hầu hết các mã bình luận trong Swift 3 :)

import Foundation 

class UISliderForScrollView:UISlider { 
    var thumbRect:CGRect { 
     let trackRect = self.trackRect(forBounds: bounds) 
     return thumbRect(forBounds: bounds, trackRect: trackRect, value: value) 
    } 

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 
     if thumbRect.contains(point) { 
      return super.hitTest(point, with: event) 
     } else { 
      return superview?.hitTest(point, with: event) 
     } 
    } 
}