Определить, является ли UIView видимым для пользователя?
Можно ли определить, является ли мой UIView
видимым для пользователя или нет?
Мой вид добавляется как subview
несколько раз в Tab Bar Controller
.
Каждый экземпляр этого представления имеет NSTimer
, который обновляет представление.
Однако я не хочу обновлять представление, которое не отображается пользователю.
Возможно ли это?
Спасибо
Ответы
Ответ 1
Вы можете проверить:
- он скрыт, проверив view.hidden
- он находится в иерархии представлений, проверив
view.superview != nil
- вы можете проверить границы представления, чтобы увидеть, находится ли он на экране
Единственное, что я могу придумать, это то, что ваш взгляд похоронен за другими и не может быть замечен по этой причине. Возможно, вам придется пройти через все взгляды, которые появляются после того, как они скрывают ваше мнение.
Ответ 2
Для всех, кто заканчивается здесь:
Чтобы определить, где UIView находится на экране, вместо проверки superview != nil
, лучше проверить, есть ли window != nil
. В первом случае возможно, что представление имеет супервизор, но супервизор не отображается на экране:
if (view.window != nil) {
// do stuff
}
Конечно, вы должны также проверить, есть ли он hidden
или имеет alpha > 0
.
Если вы не хотите, чтобы ваш NSTimer
работал, пока вид не отображается, вы должны скрыть эти представления вручную, если это возможно, и остановить таймер при скрытии представления. Тем не менее, я не совсем уверен, что вы делаете.
Ответ 3
Это определит, находится ли рамка представления в пределах всех своих супервидов (вплоть до корневого представления). Одним из практических вариантов использования является определение того, является ли дочернее представление (хотя бы частично) видимым в прокрутке.
func isVisible(view: UIView) -> Bool {
func isVisible(view: UIView, inView: UIView?) -> Bool {
guard let inView = inView else { return true }
let viewFrame = inView.convertRect(view.bounds, fromView: view)
if CGRectIntersectsRect(viewFrame, inView.bounds) {
return isVisible(view, inView: inView.superview)
}
return false
}
return isVisible(view, inView: view.superview)
}
Потенциальные улучшения:
- Respect
alpha
и hidden
.
- Respect
clipsToBounds
, поскольку представление может превышать границы его супервизора, если false.
Ответ 4
Решение, которое сработало для меня, состояло в том, чтобы сначала проверить, имеет ли представление окно, а затем перебирать супервизоры и проверять:
- вид не скрыт.
- представление находится в пределах своих ограничений.
Кажется, все хорошо.
Swift 3.0
public func isVisible(view: UIView) -> Bool {
if view.window == nil {
return false
}
var currentView: UIView = view
while let superview = currentView.superview {
if (superview.bounds).intersects(currentView.frame) == false {
return false;
}
if currentView.isHidden {
return false
}
currentView = superview
}
return true
}
Ответ 5
Я действительно хочу знать, видимо ли для пользователя вид, который вы должны учитывать:
- Является ли окно просмотра не ноль и равно самому верхнему окну
- Является ли представление и все его наблюдения альфa >= 0,01 (пороговое значение также используется UIKit для определения того, должно ли оно обрабатывать касания), а не скрыто
- Является ли z-индекс (значение стекирования) представления более высоким, чем другие представления в той же иерархии.
- Даже если индекс z ниже, он может быть виден, если другие виды сверху имеют прозрачный цвет фона, alpha 0 или скрыты.
Особенно прозрачный цвет фона в представлении спереди может представлять проблему для программной проверки. Единственный способ быть уверенным в том, чтобы сделать программный снимок представления, чтобы проверить и разбить его в своем фрейме с моментальным снимком всего экрана. Однако это не будет работать для представлений, которые не являются достаточно отличительными (например, полностью белые).
Для вдохновения см. метод isViewVisible в проект iOS Calabash-сервера
Ответ 6
В viewWillAppear установите значение "isVisible" в true, в viewWillDisappear установите значение false. Лучший способ узнать подпрограммы UITabBarController, также работает для контроллеров навигации.
Ответ 7
Это поможет вам понять, является ли ваш UIView самым верхним видом. Может быть полезно:
let visibleBool = view.superview?.subviews.last?.isEqual(view)
//have to check first whether it nil (bc it an optional)
//as well as the true/false
if let visibleBool = visibleBool where visibleBool { value
//can be seen on top
} else {
//maybe can be seen but not the topmost view
}
Ответ 8
Проверенное решение.
func isVisible(_ view: UIView) -> Bool {
if view.isHidden || view.superview == nil {
return false
}
if let rootViewController = UIApplication.shared.keyWindow?.rootViewController,
let rootView = rootViewController.view {
let viewFrame = view.convert(view.bounds, to: rootView)
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = rootView.safeAreaInsets.top
bottomSafeArea = rootView.safeAreaInsets.bottom
} else {
topSafeArea = rootViewController.topLayoutGuide.length
bottomSafeArea = rootViewController.bottomLayoutGuide.length
}
return viewFrame.minX >= 0 &&
viewFrame.maxX <= rootView.bounds.width &&
viewFrame.minY >= topSafeArea &&
viewFrame.maxY <= rootView.bounds.height - bottomSafeArea
}
return false
}
Ответ 9
попробуйте это:
func isDisplayedInScreen() -> Bool
{
if (self == nil) {
return false
}
let screenRect = UIScreen.main.bounds
//
let rect = self.convert(self.frame, from: nil)
if (rect.isEmpty || rect.isNull) {
return false
}
// 若view 隐藏
if (self.isHidden) {
return false
}
//
if (self.superview == nil) {
return false
}
//
if (rect.size.equalTo(CGSize.zero)) {
return false
}
//
let intersectionRect = rect.intersection(screenRect)
if (intersectionRect.isEmpty || intersectionRect.isNull) {
return false
}
return true
}
Ответ 10
Я сравнил @Audrey M. и @John Gibb с их решениями.
И @Audrey M. его путь показал себя лучше (раз 10).
Поэтому я использовал его, чтобы сделать его заметным.
Я сделал RxSwift Observable, чтобы получать уведомления, когда UIView становится видимым.
Это может быть полезно, если вы хотите инициировать событие просмотра баннера
import Foundation
import UIKit
import RxSwift
extension UIView {
var isVisibleToUser: Bool {
if isHidden || alpha == 0 || superview == nil {
return false
}
guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else {
return false
}
let viewFrame = convert(bounds, to: rootViewController.view)
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = rootViewController.view.safeAreaInsets.top
bottomSafeArea = rootViewController.view.safeAreaInsets.bottom
} else {
topSafeArea = rootViewController.topLayoutGuide.length
bottomSafeArea = rootViewController.bottomLayoutGuide.length
}
return viewFrame.minX >= 0 &&
viewFrame.maxX <= rootViewController.view.bounds.width &&
viewFrame.minY >= topSafeArea &&
viewFrame.maxY <= rootViewController.view.bounds.height - bottomSafeArea
}
}
extension Reactive where Base: UIView {
var isVisibleToUser: Observable<Bool> {
// Every second this will check 'isVisibleToUser'
return Observable<Int>.interval(.milliseconds(1000),
scheduler: MainScheduler.instance)
.flatMap { [base] _ in
return Observable.just(base.isVisibleToUser)
}.distinctUntilChanged()
}
}
Используйте это так:
import RxSwift
import UIKit
import Foundation
private var _disposeBag: DisposeBag?
private func _checkBannerVisibility() {
_disposeBag = DisposeBag()
bannerView.rx.isVisibleToUser
.filter { $0 }
.subscribe(onNext: { [weak self] _ in
self?._disposeBag = nil // Only trigger it once
// ... Do something with the 'isVisibleToUser' event
}).disposed(by: _disposeBag!)
}
Ответ 11
Если вы используете скрытое свойство вида, то:
view.hidden(объект C) или view.isHidden(swift) - свойство чтения/записи. Таким образом, вы можете легко читать или писать
Для быстрого 3.0
if(view.isHidden){
print("Hidden")
}else{
print("visible")
}