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.
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 < 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("_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
}
}