簡體   English   中英

使滾動條在 UIScrollView 上始終可見?

[英]Make scrollbar always visible on UIScrollView?

我需要讓滾動條在 viewDidLoad 上始終可見,以便用戶可以理解有內容要滾動。 我做了以下事情:

[myscrollView flashScrollIndicators];

但是滾動條只會在 viewDidLoad 之后出現一段時間,然后再次消失,只有在用戶觸摸屏幕時才會重新出現。

我需要使滾動條始終可見。 我該怎么做?

Apple 間接地不鼓勵在其iOS 人機界面指南中不斷顯示滾動指示器,但指南只是出於某種原因的指南,它們並未考慮到所有情況,有時您可能需要禮貌地忽略它們。

任何內容視圖的滾動指示器都是這些內容視圖的UIImageView子視圖。 這意味着您可以像訪問任何其他子視圖(即myScrollView.subviews )一樣訪問UIScrollView的滾動指示器,並像訪問任何UIImageView一樣修改滾動指示器(例如scrollIndicatorImageView.backgroundColor = [UIColor redColor]; )。

最流行的解決方案似乎是以下代碼:

#define noDisableVerticalScrollTag 836913
#define noDisableHorizontalScrollTag 836914

@implementation UIImageView (ForScrollView) 

- (void) setAlpha:(float)alpha {

    if (self.superview.tag == noDisableVerticalScrollTag) {
        if (alpha == 0 && self.autoresizingMask == UIViewAutoresizingFlexibleLeftMargin) {
            if (self.frame.size.width < 10 && self.frame.size.height > self.frame.size.width) {
                UIScrollView *sc = (UIScrollView*)self.superview;
                if (sc.frame.size.height < sc.contentSize.height) {
                    return;
                }
            }
        }
    }

    if (self.superview.tag == noDisableHorizontalScrollTag) {
        if (alpha == 0 && self.autoresizingMask == UIViewAutoresizingFlexibleTopMargin) {
            if (self.frame.size.height < 10 && self.frame.size.height < self.frame.size.width) {
                UIScrollView *sc = (UIScrollView*)self.superview;
                if (sc.frame.size.width < sc.contentSize.width) {
                    return;
                }
            }
        }
    }

    [super setAlpha:alpha];
}

@end

這最初歸功於這個來源

這為UIImageView定義了一個類別,該類別為 alpha 屬性定義了一個自定義設置器。 這是有效的,因為在UIScrollView的底層代碼中,它會將其滾動指示器的 alpha 屬性設置為 0 以隱藏它。 此時它將運行我們的類別,如果托管UIScrollView具有正確的標簽,它將忽略正在設置的值,並保持顯示。

為了使用此解決方案,請確保您的UIScrollView具有適當的標簽,例如標簽

如果您想從UIScrollView可見的那一刻起顯示滾動指示器,只需在視圖出現時閃爍​​滾動指示器即可。例如

- (void)viewDidAppear:(BOOL)animate
{
    [super viewDidAppear:animate];
    [self.scrollView flashScrollIndicators];
}

其他 SO 參考:

我想提供我的解決方案。 我不喜歡最流行的帶有類別的變體(覆蓋類別中的方法可能是不確定在運行時應該調用什么方法的原因,因為有兩個方法具有相同的選擇器)。 我用 swizzling 代替。 而且我不需要使用標簽。

將此方法添加到您的視圖控制器中,您可以在其中滾動視圖(在我的情況下為self.categoriesTableView

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // Do swizzling to turn scroll indicator always on
    // Search correct subview with scroll indicator image across tableView subviews
    for (UIView * view in self.categoriesTableView.subviews) {
        if ([view isKindOfClass:[UIImageView class]]) {
            if (view.alpha == 0 && view.autoresizingMask == UIViewAutoresizingFlexibleLeftMargin) {
                if (view.frame.size.width < 10 && view.frame.size.height > view.frame.size.width) {
                    if (self.categoriesTableView.frame.size.height < self.categoriesTableView.contentSize.height) {
                        // Swizzle class for found imageView, that should be scroll indicator
                        object_setClass(view, [AlwaysOpaqueImageView class]);
                        break;
                    }
                }
            }
        }
    }
    // Ask to flash indicator to turn it on
   [self.categoriesTableView flashScrollIndicators];
}

添加新班級

@interface AlwaysOpaqueImageView : UIImageView
@end

@implementation AlwaysOpaqueImageView

- (void)setAlpha:(CGFloat)alpha {
    [super setAlpha:1.0];
}

@end

滾動指示器(在這種情況下是垂直滾動指示器)將始終顯示在屏幕上。

2019 年 11 月更新

從 iOS 13 開始UIScrollView子類發生了變化。 現在滾動指示器繼承自UIView並擁有自己的私有類,稱為_UIScrollViewScrollIndicator 這意味着,它們現在不是UIImageView子類,因此舊方法將不再起作用。

此外,我們無法實現_UIScrollViewScrollIndicator子類,因為它是私有類,我們無權訪問它。 所以唯一的解決方案是使用運行時。 現在要支持 iOS 13 及更早版本,請執行以下步驟:

  1. 將此方法添加到您的視圖控制器中,您可以在其中滾動視圖(在我的情況下為self.categoriesTableView
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // Do swizzling to turn scroll indicator always on
    // Search correct subview with scroll indicator image across tableView subviews
    for (UIView * view in self.categoriesTableView.subviews) {
        if ([view isKindOfClass:[UIImageView class]]) {
            if (view.alpha == 0 && view.autoresizingMask == UIViewAutoresizingFlexibleLeftMargin) {
                if (view.frame.size.width < 10 && view.frame.size.height > view.frame.size.width) {
                    if (self.categoriesTableView.frame.size.height < self.categoriesTableView.contentSize.height) {
                        // Swizzle class for found imageView, that should be scroll indicator
                        object_setClass(view, [AlwaysOpaqueImageView class]);
                        break;
                    }
                }
            }
        } else if ([NSStringFromClass(view.class) isEqualToString:@"_UIScrollViewScrollIndicator"]) {
            if (view.frame.size.width < 10 && view.frame.size.height > view.frame.size.width) {
                if (self.categoriesTableView.frame.size.height < self.categoriesTableView.contentSize.height) {
                    // Swizzle class for found scroll indicator, (be sure to create AlwaysOpaqueScrollIndicator in runtime earlier!)
                    // Current implementation is in AlwaysOpaqueScrollTableView class
                    object_setClass(view, NSClassFromString(@"AlwaysOpaqueScrollIndicator"));
                    break;
                }
            }
        }
    }
    // Ask to flash indicator to turn it on
    [self.categoriesTableView flashScrollIndicators];
}
  1. 添加新類(這是針對 13 之前的 iOS)
@interface AlwaysOpaqueImageView : UIImageView
@end

@implementation AlwaysOpaqueImageView

- (void)setAlpha:(CGFloat)alpha {
    [super setAlpha:1.0];
}

@end
  1. 在代碼中的某處添加這些方法(與步驟 1 中的視圖控制器相同,或者添加到所需的UIScrollView子類)。
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // Create child class from _UIScrollViewScrollIndicator since it is private
        Class alwaysOpaqueScrollIndicatorClass =  objc_allocateClassPair(NSClassFromString(@"_UIScrollViewScrollIndicator"), "AlwaysOpaqueScrollIndicator", 0);
        objc_registerClassPair(alwaysOpaqueScrollIndicatorClass);

        // Swizzle setAlpha: method of this class to custom
        Class replacementMethodClass = [self class];

        SEL originalSelector = @selector(setAlpha:);
        SEL swizzledSelector = @selector(alwaysOpaque_setAlpha:);

        Method originalMethod = class_getInstanceMethod(alwaysOpaqueScrollIndicatorClass, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(replacementMethodClass, swizzledSelector);

        BOOL didAddMethod =
            class_addMethod(alwaysOpaqueScrollIndicatorClass,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(alwaysOpaqueScrollIndicatorClass,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)alwaysOpaque_setAlpha:(CGFloat)alpha {
    [self alwaysOpaque_setAlpha:1.0];
}

此步驟在運行時創建名為AlwaysOpaqueScrollIndicator_UIScrollViewScrollIndicator的子類, _UIScrollViewScrollIndicator setAlpha:方法實現混合到alwaysOpaque_setAlpha:

不要忘記添加

#import <objc/runtime.h>

到您插入此代碼的文件。 感謝@Smartcat 對此的提醒

我不知道這是否會奏效。 但只是給你一個提示。

Scrollview 內的滾動條是一個 Imageview。 這是 UIScrollview 的子視圖

所以得到UIscrollview的Scrollbar Imageview。 然后嘗試將該圖像屬性隱藏為 NO 或更改 Alpha 值

static const int UIScrollViewHorizontalBarIndexOffset = 0;
static const int UIScrollViewVerticalBarIndexOffset = 1;
-(UIImageView *)scrollbarImageViewWithIndex:(int)indexOffset 
{
    int viewsCount = [[yourScrollview subviews] count];
    UIImageView *scrollBar = [[yourScrollview subviews] objectAtIndex:viewsCount - indexOffset - 1];
    return scrollBar;
}

-(void) viewDidLoad
{
    //Some Code
    //Get Scrollbar
    UIImageView *scrollBar = [self scrollbarImageViewWithIndex: UIScrollViewVerticalBarIndexOffset];

    //The try setting hidden property/ alpha value
    scrollBar.hidden=NO;
}

這里得到參考

這是@Accid Bright答案的Swift 版本:

class AlwaysOpaqueImageView: UIImageView {

    override var alpha: CGFloat {
        didSet {
            alpha = 1
        }
    }

    static func setScrollbarToAlwaysVisible(from scrollView: UIScrollView) {
        // Do swizzling to turn scroll indicator always on
        // Search correct subview with scroll indicator image across tableView subviews
        for view in scrollView.subviews {
            if view.isKind(of: UIImageView.self),
                view.alpha == 0 && view.autoresizingMask == UIView.AutoresizingMask.flexibleLeftMargin,
                view.frame.size.width < 10 && view.frame.size.height > view.frame.size.width,
                scrollView.frame.size.height < scrollView.contentSize.height {
                // Swizzle class for found imageView, that should be scroll indicator
                object_setClass(view, AlwaysOpaqueImageView.self)
                break
            }
        }

        // Ask to flash indicator to turn it on
        scrollView.flashScrollIndicators()
    }
}

一個區別是設置滾動條是作為靜態方法提取出來的。

唯一對我有用的是:

定時器閃光指示器修復

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM