滚动条在UIScrollView上始终可见?

Hon*_*ney 20 iphone cocoa-touch uiscrollview uikit ios

我需要在viewDidLoad上始终显示滚动条,以便用户可以理解有滚动的内容.我做了以下事情:

[myscrollView flashScrollIndicators];
Run Code Online (Sandbox Code Playgroud)

但是滚动条只会在viewDidLoad之后显示一段时间并再次消失,只是在用户触摸屏幕时重新出现.

我需要让滚动条始终可见.我该怎么做?

Ell*_*rry 19

Apple间接阻止在其iOS人机界面指南中不断显示滚动指示器,但指南只是原因的指导原则,它们不考虑每个场景,有时您可能需要礼貌地忽略它们.

任何内容视图的滚动指示符都是UIImageView这些内容视图的子视图.这意味着您可以像访问UIScrollView任何其他子视图(即myScrollView.subviews)一样访问a的滚动指示器,并像修改任何滚动指示器一样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
Run Code Online (Sandbox Code Playgroud)

这最初归功于此来源.

这定义了一个类别,用于UIImageView定义alpha属性的自定义setter.这是有效的,因为在底层代码中的某些时候UIScrollView,它会将其滚动指示器的alpha属性设置为0以隐藏它.此时它将运行我们的类别,如果托管UIScrollView具有正确的标记,它将忽略正在设置的值,并将其显示.

为了使用此解决方案,请确保您UIScrollView拥有适当的标签,例如 标签

如果要显示滚动指示器的UIScrollView可见时间,只需在视图出现时闪烁​​滚动指示器即.eg

- (void)viewDidAppear:(BOOL)animate
{
    [super viewDidAppear:animate];
    [self.scrollView flashScrollIndicators];
}
Run Code Online (Sandbox Code Playgroud)

其他SO参考:

  • 为了避免在64位上出现问题,请使用(CGFloat)alpha而不是(float)alpha。请参阅http://stackoverflow.com/q/20855176/1873374。 (2认同)

Acc*_*ght 5

我想提供我的解决方案。我不喜欢类别中最流行的变体(类别中的覆盖方法可能是不确定的原因,因为在运行时应该调用哪种方法,因为有两个方法具有相同的选择器)。我改用毛毛雨。而且我也不需要使用标签。

将此方法添加到您具有滚动视图的视图控制器中(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];
}
Run Code Online (Sandbox Code Playgroud)

新增班级

@interface AlwaysOpaqueImageView : UIImageView
@end

@implementation AlwaysOpaqueImageView

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

@end
Run Code Online (Sandbox Code Playgroud)

滚动指示器(在这种情况下为垂直滚动指示器)将始终在屏幕上。

更新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];
}
Run Code Online (Sandbox Code Playgroud)
  1. 添加新类(适用于13之前的iOS)
@interface AlwaysOpaqueImageView : UIImageView
@end

@implementation AlwaysOpaqueImageView

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

@end
Run Code Online (Sandbox Code Playgroud)
  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];
}
Run Code Online (Sandbox Code Playgroud)

此步骤将在运行时创建被_UIScrollViewScrollIndicator调用的子类,AlwaysOpaqueScrollIndicator并将swizzle setAlpha:方法的实现实现为alwaysOpaque_setAlpha:

不要忘记添加

#import <objc/runtime.h>

您插入此代码的文件。感谢@Smartcat提醒您

  • 在iOS 11中运行良好。需要#include &lt;objc / runtime.h&gt;。 (2认同)