【问题标题】:Make scrollbar always visible on UIScrollView?让滚动条在 UIScrollView 上始终可见?
【发布时间】:2012-11-21 17:50:04
【问题描述】:

我需要在 viewDidLoad 上创建一个始终可见的滚动条,以便用户可以了解有要滚动的内容。我做了以下事情:

[myscrollView flashScrollIndicators];

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

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

【问题讨论】:

  • Cudnt 明白你,滚动视图是否出现在触摸?另外滚动条是如何消失的?你有没有设置一个计时器,还是你要去下一个视图然后回来??
  • 是的,滚动视图出现在触摸屏上。当我使用 [myscrollView flashScrollIndicators];它们出现在 viewLoad 上,然后在几秒钟后消失。我想让它们始终可见..不,我不会来回走动。
  • 所以你不希望他们消失仪式?

标签: iphone ios cocoa-touch uiscrollview uikit


【解决方案1】:

Apple 间接不鼓励在其 iOS Human Interface Guidelines 中不断显示滚动指示符,但准则只是出于某种原因的准则,它们并不适用于所有情况,有时您可能需要礼貌地忽略它们。

任何内容视图的滚动指示器都是这些内容视图的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

最初归功于this source

这为UIImageView 定义了一个类别,该类别为 alpha 属性定义了一个自定义设置器。这是因为在UIScrollView 的底层代码中的某个时刻,它将滚动指示器的 alpha 属性设置为 0 以隐藏它。此时它将遍历我们的类别,如果托管UIScrollView 具有正确的标签,它将忽略正在设置的值,将其显示出来。

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

如果您想从其UIScrollView 可见的那一刻起显示滚动指示器,只需在视图出现时闪烁​​滚动指示器即可。例如

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

其他 SO 参考:

【讨论】:

  • 我会警告这一点,说它依赖于私有实现细节(指示符是UIImageView 对象),这可能并不总是正确的。事实上,这是草皮法则,它总是会在你最意想不到的时候打破,通常是在新的 iOS 版本中。因此,如果您确实使用它,请记住您的里程可能会有所不同。
  • 为避免在 64 位上出现问题,请使用 (CGFloat)alpha 而不是 (float)alpha。见stackoverflow.com/q/20855176/1873374
【解决方案2】:

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

将此方法添加到具有滚动视图的视图控制器(在我的情况下为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 并有自己的 private 类,称为 _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];
}

这一步在运行时创建_UIScrollViewScrollIndicator 的子类AlwaysOpaqueScrollIndicator,并将setAlpha: 方法实现转换为alwaysOpaque_setAlpha:

别忘了添加

#import &lt;objc/runtime.h&gt;

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

【讨论】:

  • 使用 iOS 10,这是可行的。如果要设置水平滚动条,请使用: UIViewAutoresizing.flexibleTopMargin(Swift 3.0) 或 UIViewAutoresizingFlexibleTopMargin (Obj-C)
  • 在 iOS 11 中运行良好。需要#include
【解决方案3】:

我不知道这是否可行。但只是给你一个提示。

Scrollview里面的Scrollbar是一个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;
}

得到here的引用

【讨论】:

  • 实际上在你给出的例子中我已经检查过了。它是关于改变滚动条的颜色。但我需要它们在视图上不断显示(总是可见)
  • 我知道.. 只是获得该视图的提示。既然你得到了滚动条对象.. 只需通过调整隐藏属性或 alpha 值来操作它.. 这样它就会一直显示。
  • 我尝试了隐藏属性。但这不起作用..你能告诉我如何使用 alpha 值吗?
  • 仅供参考。这可以为您尝试。既然你得到了滚动条对象。尝试修改它。但是当滚动条发生动作时,所有属性都会被修改。因此,请在操作结束时修改滚动条。
【解决方案4】:

这是@Accid Bright 的answer 的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()
    }
}

一个区别是设置滚动条是作为静态方法提取出来的。

【讨论】:

    猜你喜欢
    • 2013-12-18
    • 2013-10-19
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 2010-11-15
    • 2011-09-15
    • 2023-02-01
    • 1970-01-01
    相关资源
    最近更新 更多