est-il possible de déterminer si mon UIView
est visible ou non pour l'utilisateur?
Ma vue est ajoutée en tant que subview
plusieurs fois dans un Tab Bar Controller
.
Chaque instance de cette vue a un NSTimer
qui met à jour la vue.
Cependant, je ne veux pas mettre à jour une vue qui n'est pas visible pour l'utilisateur.
Est-ce possible?
Merci
Vous pouvez vérifier si:
view.superview != nil
La seule autre chose à laquelle je peux penser, c'est si votre vue est enfouie derrière les autres et ne peut pas être vue pour cette raison. Vous devrez peut-être parcourir toutes les vues qui suivent pour voir si elles obscurcissent votre vue.
Pour toute autre personne qui se retrouve ici:
Pour déterminer si une UIView est à l'écran quelque part, plutôt que de vérifier superview != nil
, il vaut mieux vérifier si window != nil
. Dans le premier cas, il est possible que la vue ait une vue d'ensemble mais que la vue d'ensemble ne soit pas à l'écran:
if (view.window != nil) {
// do stuff
}
Bien sûr, vous devez également vérifier s'il s'agit de hidden
ou s'il a un alpha > 0
.
En ce qui concerne le fait de ne pas vouloir que votre NSTimer
s'exécute alors que la vue n'est pas visible, vous devez masquer ces vues manuellement si possible et faire arrêter la minuterie lorsque la vue est masquée. Cependant, je ne suis pas du tout sûr de ce que vous faites.
Cela déterminera si le cadre d'une vue est dans les limites de tous ses superviews (jusqu'à la vue racine). Un cas d'utilisation pratique consiste à déterminer si une vue enfant est (au moins partiellement) visible dans une vue de défilement.
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)
}
Améliorations potentielles:
alpha
et hidden
.clipsToBounds
, car une vue peut dépasser les limites de sa vue d'ensemble si elle est fausse.La solution qui a fonctionné pour moi a été de vérifier d'abord si la vue a une fenêtre, puis de parcourir les superviews et de vérifier si:
Semble bien fonctionner jusqu'à présent.
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
}
Si vous voulez vraiment savoir si une vue est visible pour l'utilisateur, vous devez prendre en compte les éléments suivants:
En particulier, la couleur d'arrière-plan transparente des vues de devant peut poser un problème à vérifier par programme. La seule façon d'être vraiment sûr est de faire un instantané programmatique de la vue pour la vérifier et la différencier dans son cadre avec l'instantané de tout l'écran. Cela ne fonctionnera cependant pas pour les vues qui ne sont pas suffisamment distinctives (par exemple, entièrement blanches).
Pour l'inspiration, voir la méthode isViewVisible dans le projet iOS Calabash-server
Dans viewWillAppear, définissez une valeur "isVisible" sur true, dans viewWillDisappear, définissez-la sur false. La meilleure façon de savoir pour une sous-vue UITabBarController, fonctionne également pour les contrôleurs de navigation.
Cela peut vous aider à déterminer si votre UIView est la vue la plus haute. Peut être utile:
let visibleBool = view.superview?.subviews.last?.isEqual(view)
//have to check first whether it's nil (bc it's 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
}
Si vous utilisez une propriété de vue cachée, alors:
view.hidden (Objective C) ou view.isHidden (Swift) est une propriété en lecture/écriture. Vous pouvez donc facilement lire ou écrire
Pour Swift 3.
if(view.isHidden){
print("Hidden")
}else{
print("visible")
}
Solution testée.
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
}
essaye ça:
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
}