Подтвердить что ты не робот

Определить, является ли UIView видимым для пользователя?

Можно ли определить, является ли мой UIView видимым для пользователя или нет?

Мой вид добавляется как subview несколько раз в Tab Bar Controller.

Каждый экземпляр этого представления имеет NSTimer, который обновляет представление.

Однако я не хочу обновлять представление, которое не отображается пользователю.

Возможно ли это?

Спасибо

4b9b3361

Ответ 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")
}