Utiliser une Custom UIView dans UIView Outlet en code ?

Bonjour tout le monde,

J’ai une classe MonIcone qui est en réalité une UIView. Cette classe représente en réalité un icône grâce à la fonction draw et ensuite du code généré par PaintCode.

J’aimerai utiliser cette classe dans une View de mon Storyboard.
Si j’indique cette classe (MonIcone) comme classe d’une vue créée dans mon Storyboard, ça fonctionne. Par contre, si j’essaye de faire la même chose dans mon code:

ui_maVue = MonIcone()

La vue reste vide, et je n’ai pas mon icône qui apparait…

Je précise la vue MonIcone n’a pas de .xib correspondant.
Toutes les informations que je trouve sur Internet sont relatives à l’utilisation d’un fichier .xib correspondant à la classe UIView personnalisée.

Une idée ?

Bonne journée,

Alexandre

Salut Alexandre,

Comment utilises-tu ta vue au sein de ta classe ?
Peux-tu donner tout le code de la création de la vue à son ajout dans la superview ainsi que la définition de sa frame ?

Bonjour @iMrMaximus

Voici la classe de ma vue (qui représente un icône de cadenas):

class LockIcon: UIView {
    
    override func draw(_ rect: CGRect) {
        drawLockIcon(lockIconDimension: 30)
    }
    
    func drawLockIcon(lockIconDimension: CGFloat = 55) {
        
        let THEME = ThemeManager.THEME
        
        //// General Declarations
        // This non-generic function dramatically improves compilation times of complex expressions.
        func fastFloor(_ x: CGFloat) -> CGFloat { return floor(x) }
        
        //// Color Declarations
        let lockIconColor = THEME.getIconBackgroundColor
        
        //// Frames
        let frame = CGRect(x: 0, y: -0, width: lockIconDimension, height: lockIconDimension)
        
        //// Subframes
        let iconGroup: CGRect = CGRect(x: frame.minX + 8.3, y: frame.minY + 1, width: fastFloor((frame.width - 8.3) * 0.83667 + 8.42) - 7.92, height: fastFloor((frame.height - 1) * 0.97533 + 0.83) - 0.33)
        
        
        //// iconGroup
        //// icon Drawing
        let iconPath = UIBezierPath()
        iconPath.move(to: CGPoint(x: iconGroup.minX + 0.97062 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.88689 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.88689 * iconGroup.width, y: iconGroup.minY + 0.27246 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.50000 * iconGroup.width, y: iconGroup.minY + 0.00000 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.88689 * iconGroup.width, y: iconGroup.minY + 0.12222 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.71332 * iconGroup.width, y: iconGroup.minY + 0.00000 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.11311 * iconGroup.width, y: iconGroup.minY + 0.27246 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.28666 * iconGroup.width, y: iconGroup.minY + 0.00000 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.11311 * iconGroup.width, y: iconGroup.minY + 0.12222 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.11311 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.02938 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.00000 * iconGroup.width, y: iconGroup.minY + 0.44074 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.01316 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.00000 * iconGroup.width, y: iconGroup.minY + 0.42931 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.00000 * iconGroup.width, y: iconGroup.minY + 0.97931 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.02938 * iconGroup.width, y: iconGroup.minY + 1.00000 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.00000 * iconGroup.width, y: iconGroup.minY + 0.99073 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.01316 * iconGroup.width, y: iconGroup.minY + 1.00000 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.97062 * iconGroup.width, y: iconGroup.minY + 1.00000 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 1.00000 * iconGroup.width, y: iconGroup.minY + 0.97931 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.98684 * iconGroup.width, y: iconGroup.minY + 1.00000 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 1.00000 * iconGroup.width, y: iconGroup.minY + 0.99073 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 1.00000 * iconGroup.width, y: iconGroup.minY + 0.44074 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.97062 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 1.00000 * iconGroup.width, y: iconGroup.minY + 0.42930 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.98687 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.close()
        iconPath.move(to: CGPoint(x: iconGroup.minX + 0.43572 * iconGroup.width, y: iconGroup.minY + 0.76758 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.40605 * iconGroup.width, y: iconGroup.minY + 0.71941 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.41659 * iconGroup.width, y: iconGroup.minY + 0.75498 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.40605 * iconGroup.width, y: iconGroup.minY + 0.73788 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.50000 * iconGroup.width, y: iconGroup.minY + 0.65325 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.40605 * iconGroup.width, y: iconGroup.minY + 0.68294 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.44819 * iconGroup.width, y: iconGroup.minY + 0.65325 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.59395 * iconGroup.width, y: iconGroup.minY + 0.71941 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.55180 * iconGroup.width, y: iconGroup.minY + 0.65325 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.59395 * iconGroup.width, y: iconGroup.minY + 0.68293 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.56428 * iconGroup.width, y: iconGroup.minY + 0.76758 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.59395 * iconGroup.width, y: iconGroup.minY + 0.73788 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.58342 * iconGroup.width, y: iconGroup.minY + 0.75498 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.55497 * iconGroup.width, y: iconGroup.minY + 0.78269 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.55835 * iconGroup.width, y: iconGroup.minY + 0.77149 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.55497 * iconGroup.width, y: iconGroup.minY + 0.77696 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.55497 * iconGroup.width, y: iconGroup.minY + 0.85285 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.49999 * iconGroup.width, y: iconGroup.minY + 0.89156 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.55497 * iconGroup.width, y: iconGroup.minY + 0.87419 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.53030 * iconGroup.width, y: iconGroup.minY + 0.89156 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.44500 * iconGroup.width, y: iconGroup.minY + 0.85285 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.46967 * iconGroup.width, y: iconGroup.minY + 0.89156 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.44500 * iconGroup.width, y: iconGroup.minY + 0.87419 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.44500 * iconGroup.width, y: iconGroup.minY + 0.78269 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.43572 * iconGroup.width, y: iconGroup.minY + 0.76758 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.44502 * iconGroup.width, y: iconGroup.minY + 0.77696 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.44165 * iconGroup.width, y: iconGroup.minY + 0.77149 * iconGroup.height))
        iconPath.close()
        iconPath.move(to: CGPoint(x: iconGroup.minX + 0.50000 * iconGroup.width, y: iconGroup.minY + 0.14686 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.67833 * iconGroup.width, y: iconGroup.minY + 0.27246 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.59833 * iconGroup.width, y: iconGroup.minY + 0.14686 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.67833 * iconGroup.width, y: iconGroup.minY + 0.20320 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.67833 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.32167 * iconGroup.width, y: iconGroup.minY + 0.42005 * iconGroup.height))
        iconPath.addLine(to: CGPoint(x: iconGroup.minX + 0.32167 * iconGroup.width, y: iconGroup.minY + 0.27246 * iconGroup.height))
        iconPath.addCurve(to: CGPoint(x: iconGroup.minX + 0.50000 * iconGroup.width, y: iconGroup.minY + 0.14686 * iconGroup.height), controlPoint1: CGPoint(x: iconGroup.minX + 0.32167 * iconGroup.width, y: iconGroup.minY + 0.20321 * iconGroup.height), controlPoint2: CGPoint(x: iconGroup.minX + 0.40167 * iconGroup.width, y: iconGroup.minY + 0.14686 * iconGroup.height))
        iconPath.close()
        lockIconColor.setFill()
        iconPath.fill()
    }
    
}

Voici le code qui l’utilise (c’est en réalité une classe qui hérite de TableViewCell):
(J’appelle le commonInit depuis mon TableViewController; l’affichage du titre et sous-titre fonctionne bien)

@IBOutlet weak var ui_titleLabel: UILabel!
@IBOutlet weak var ui_subtitleLabel: UILabel!
@IBOutlet weak var ui_iconView: UIView!

override func awakeFromNib() {
    super.awakeFromNib()
}

func commonInit(title:String, subtitle:String) {
    ui_titleLabel.text = title
    ui_subtitleLabel.text = subtitle
    ui_iconView = LockIcon()
}

Dans mon Storyboard, si je fais ceci:

29

Cela fonctionne bien.

Merci du temps que tu m’accordes :slight_smile:

Si tu veux utiliser cette vue directement dans le code sans storyboard, ne la défini pas comme un outlet et défini-la directement en tant que class de ta vue comme suit.
Remplace :
@IBOutlet weak var ui_iconView: UIView!
Par :
var ui_iconView: LockIcon!

Ensuite, il faut que tu ajout cette vue au sein de la vue de la cellule (la contentView si je ne me trompe pas) et définissant sa position.

Dis-moi ce que ça donne :wink:

Oui, dans le cas où je ne mettrais que cet icône, je pensais le faire directement dans le Storyboard (puisque ça, ça fonctionne), mais dans ce cas-ci, j’aimerai, en fonction de plusieurs éléments, pouvoir mettre l’un ou l’autre icône.

Donc, faire:

var ui_iconeView:LockIcon!

ne va pas fonctionner (ou alors je dois le faire pour chacun de mes icônes ?)

Ok, dans ce cas, jusqu’à ce qu’une meilleure idée ne me vienne, je t’invite à créer une instance de cet icône, et de la mettre dans ta vue ui_iconView.

je t’invite à créer une instance de cet icône, et de la mettre dans ta vue ui_iconView.

Comme ceci ? (Je ne suis pas sur de comprendre ce que tu entends par là)

var lockIcon:LockIcon = LockIcon()
ui_iconView = lockIcon

Ce qui revient au même que:

ui_iconView = LockIcon()

Non ?
Mais cela ne fonctionne pas…

Non, plutôt comme ceci :

var lockIcon: LockIcon = LockIcon()
ui_iconView.addSubview(lockIcon)
lockIcon.frame.size = ui_iconView.frame.size
2 « J'aime »

J’essaye ça de suite, et je te tiens au courant. :slight_smile:

Alors, effectivement, ça fonctionne bien, mon icône s’affiche, mais j’ai un soucis/une question par rapport à ça:

Si je scroll ma TableView vers le bas, puis vers le haut plusieurs fois, la mémoire utilisée par l’application ne cesse d’augmenter sans jamais redescendre, comme si les icônes étaient créés de plus en plus, les un au dessus des autres, et donc prenait de l’espace mémoire… Une idée du pourquoi ?

08

J’avais déjà eu le soucis avec une TableView qui contenait beaucoup de données, et au bout d’un moment, c’est l’application qui crash… Une fuite mémoire ?

Non, c’est juste qu’à chaque affichage d’une cellule affichant l’icône, cette dernière est ré-instanciée, donc de la mémoire est utilisée.
Pour se fait, tu sors la variable lockIcon en faisant une variable d’instance (juste en dessous de tes outlet) comme suit :
var lockIcon: LockIcon!

Et pour les 2 lignes restantes, tu les mets dans un joli if “non instanciée” comme ça :

if self.lockIcon == nil {
    ui_iconView.addSubview(self.lockIcon)
    self.lockIcon.frame.size = ui_iconView.frame.size
}
2 « J'aime »

Je n’avais pas vu ça de cette façon, mais dis comme ça, c’est effectivement évident… :grin:

Pour simplifier encore un peu plus le code, je peux aussi le faire de cette manière, non ?

// Juste en dessous des Outlets
let lockIcon:LockIcon = LockIcon()

// Dans le code
ui_iconView.addSubview(self.lockIcon)
self.lockIcon.frame.size = ui_iconView.frame.size

Puisque le lockIcon serait présent dans ma vue dans 99% des cas, et le charger une fois au départ ne prends pas tant de mémoire que ça.

Alors, d’abord, j’ai commis une erreur sur le fait que j’ai oublié d’instancier la variable lockIcon :confused:.

Ensuite, oui, tu peux l’instancier une fois pour toute, par contre, fais attention où tu mets le code d’ajout de l’icône parce que si, à chaque affichage de la cellule, l’icône est mise dans ui_iconView, tu auras toujours la fuite mémoire.
(Parce que oui, 2e erreur de ma part, je pense que c’en est une…)

C’est pour cela que j’aurai mis le bout de code dans un if, après, si t’es sûr de l’unique passage, vas-y, l’exécution du code en sera optimisé :wink:

1 « J'aime »

Je vais faire des tests et voir pour ne pas avoir de problème :slight_smile:
Si je fais comme j’ai mis dans le poste précédent, à priori, on dirait qu’il n’y a plus de soucis, mais je vais continuer à tester, pour être sur!

Un grand merci pour ton aide :smile:

Je t’en prie, c’était avec plaisir :wink:

N’hésite pas à revenir vers moi pour me dire la conséquence de l’absence de if (vis-à-vis de la fuite mémoire).
J’aimerais bien savoir :slight_smile:

Le temps d’implémenter les autres icônes et de finir un ou deux trucs, et je test tout ça, ainsi on pourra voir si le problème de la fuite persiste ou non!

Je te tiens au courant :slight_smile:

Bon ben voilà @iMrMaximus :slight_smile:

J’ai ajouté les autres icônes dont j’ai besoin pour la TableView, et j’ai utilisé le code que j’ai mis plus haut (sans le if), et il n’y a pas l’air d’avoir de soucis au niveau de l’utilisation de la mémoire (sur un smartphone physique, avant d’afficher la TableView, je suis à 27MB, et une fois affichée je passe à 29, mais ensuite, je peux scroller autant de fois que je le souhaite, je reste à 29)

Bonne nouvelle donc :smile:

Encore merci!

2 « J'aime »

Super good news ! :wink:

1 « J'aime »

Petite remarque :
J’ai vu que tu as créé ton icône toi-même avec les outils d’Apple, dans un sens, je trouve ça bien parce que, comme ça, tu maîtrises cette partie là et tu peux faire l’icône que tu veux.
Cependant, je me demandais, as-tu pensé à passer par une image à stocker dans ton app ?

Oui, j’ai pensé le faire en image “simple”, mais je me suis rabattu sur une classe créée comme j’ai fait pour pouvoir changer dynamiquement la couleur de l’image en fonction du thème de mon application.
(J’ai découvert par la suite qu’il était également possible de le faire avec les images, mais je ne sais pas ce qui est le mieux ?)

Et puis, un autre avantage avec le code plutôt qu’une image, c’est la responsivité sur les appareils qui est plus précis (je trouve), enfin, je suis moins limité. :slight_smile: