英文:
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
的子视图,有一个显示 attributedTitle
的 UILabel
。你可以不断尝试将该标签的 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 < 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
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论