iOS UIRefreshControl子视图的不透明度类似于attributedTitle的不透明度。

huangapple go评论73阅读模式
英文:

iOS UIRefreshControl subview opacity similar to the attributedTitle opacity

问题

我有一个UIRefreshControl的子类,除了标题之外,还向其子视图添加了一个自定义控件。它工作得很好,除了一个问题,基于拉动距离的不透明度仅应用于文本,而不适用于子视图。

class CustomRefreshControl: UIRefreshControl {
    private let customView = MyCustomView()
    
    override init() {
        super.init()
        addSubview(customView)
    }

我如何根据UIRefreshControl的拉动距离动态更改customView的不透明度?我希望它与attributedTitle的不透明度相同。

英文:

I have a UIRefreshControl subclass that in addition to the title adds a custom control to its subviews. It works great except of one thing, the pull-based-on-distance-opacity is applied only to the text, and not the subview.

class CustomRefreshControl: UIRefreshControl {
    private let customView = MyCustomView()
    
    override init() {
        super.init()
        addSubview(customView)
    }

How can I dynamically change the customView opacity based on the UIRefreshControl pull distance? I want it to be the same as the attributedTitle opacity.

答案1

得分: 1

我认为没有专门的方法来匹配属性标题的不透明度。我甚至认为 UIRefreshControl 并不是设计成可以以这种方式修改的,添加随机子视图到其中。

我建议创建一个类似于 UIRefreshControl 的自定义视图,根据滚动视图的 contentOffset 来更改其不透明度和框架。可以参考这里

假设只有垂直滚动的滚动视图,你可以这样做:

// CustomRefreshControl 只是一个 UIView 子类
var refreshControl: CustomRefreshControl!

// 这在一个 UIViewController 中,但如果你想要的话,可以轻松包装一个自定义的 UIScrollView。
override func viewDidLoad() {
    scrollView.delegate = self
    refreshControl = .init(frame: .zero)
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y < 0 {
        if refreshControl.superview == nil {
            scrollView.addSubview(refreshControl)
            scrollView.sendSubviewToBack(refreshControl)
        }
        // 更新不透明度
        // 可以将其作为 CustomRefreshControl 中的一个方法,以封装逻辑
        refreshControl.layer.opacity = Float(-scrollView.contentOffset.y / 100)
        refreshControl.frame = CGRect(x: 0, y: scrollView.contentOffset.y, width: scrollView.frame.width, height: 100)
    } else {
        refreshControl.removeFromSuperview()
    }
}

我不建议这样做,但一个非常巧妙的解决方法是检查 UIRefreshControl 的视图层次结构,找到其中类型为 _UIRefreshControlModernContentView 的子视图。下划线前缀表示这不是一个稳定的 API,可能会在未来的 iOS 版本中破坏。

作为 _UIRefreshControlModernContentView 的子视图,有一个显示 attributedTitleUILabel。你可以不断尝试将该标签的 layer.opacity 设置为你自定义视图的 layer.opacity

由于 attributedTitle 的标签在滚动时似乎会移动,layoutSubviews 方法将被调用,你可以在其中执行以下操作:

override func layoutSubviews() {
    if let contentViewClass = NSClassFromString("_UIRefreshControlModernContentView"),
       let contentView = subviews.first(where: { $0.isKind(of: contentViewClass) }),
       let label = contentView.subviews.first(where: { $0 is UILabel }) {
        customView.layer.opacity = label.layer.opacity
    }
}

希望这对你有所帮助。

英文:

I don't think there is a dedicated way to match the opacity of the attributed title. I don't even think that UIRefreshControl is designed to be modified in this way, adding random subviews to it.

I would suggest creating your own view that resembles a UIRefreshControl, that changes its opacity and frame depending on the scroll view's contentOffset. See here for a starting point.

Assuming a vertical-scroll only scroll view, you could do something like:

// CustomRefreshControl is just a UIView subclass
var refreshControl: CustomRefreshControl!

// this is in a UIViewController, but it should be trivial to wrap 
// a custom UIScrollView instead, if you want that.
override func viewDidLoad() {
    scrollView.delegate = self
    refreshControl = .init(frame: .zero)
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y &lt; 0 {
        if refreshControl.superview == nil {
            scrollView.addSubview(refreshControl)
            scrollView.sendSubviewToBack(refreshControl)
        }
        // update the opacity
        // could be a method in CustomRefreshControl instead, to
        // encapsulate the logic
        refreshControl.layer.opacity = Float(-scrollView.contentOffset.y / 100)
        refreshControl.frame = CGRect(x: 0, y: scrollView.contentOffset.y, width: scrollView.frame.width, height: 100)
    } else {
        refreshControl.removeFromSuperview()
    }
}

I wouldn't recommend this, but a really hacky solution would be to inspect the view hierarchy of UIRefreshControl and find that it has a subview of type _UIRefreshControlModernContentView. The underscore prefix suggests that this is not a stable API, so could break in future iOS versions.

As a subview of _UIRefreshControlModernContentView, there is a UILabel displaying the attributedTitle. You can constantly try to set that label's layer.opacity to your custom view's layer.opacity.

Since the label for attributedTitle appears to move when you scroll, layoutSubviews will be called, and you can do this in there:

override func layoutSubviews() {
    if let contentViewClass = NSClassFromString(&quot;_UIRefreshControlModernContentView&quot;),
       let contentView = subviews.first(where: { $0.isKind(of: contentViewClass) }),
       let label = contentView.subviews.first(where: { $0 is UILabel }) {
        customView.layer.opacity = label.layer.opacity
    }
}

huangapple
  • 本文由 发表于 2023年7月14日 07:42:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76683880.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定