Étant donné que les objets sont des types de référence et non des types de valeur, si vous définissez un UIView
égal à un autre UIView
, les vues sont le même objet. Si vous modifiez l'un, vous modifierez également l'autre.
J'ai une situation intéressante où je voudrais ajouter un UIView
comme sous-vue dans une autre vue, puis j'apporte quelques modifications, et ces modifications ne devraient pas affecter le UIView
d'origine. Comment puis-je faire une copie du UIView
pour pouvoir m'assurer que j'ajoute cette copie en tant que sous-vue au lieu d'une référence au UIView
d'origine?
Notez que je ne peux pas recréer la vue de la même manière que l'original a été créé, j'ai besoin d'un moyen de créer une copie avec n'importe quel objet UIView
.
Vous ne pouvez pas copier arbitrairement un objet. Seuls les objets qui implémentent le protocole NSCopying
peuvent être copiés.
Cependant, il existe une solution de contournement: comme UIView
s peut être sérialisé sur disque (par exemple pour charger à partir d'un XIB), vous pouvez utiliser NSKeyedArchiver
et NSKeyedUnarchiver
pour créer un sérialisé NSData
décrivant votre vue, puis désérialisez-la à nouveau pour obtenir un objet indépendant mais identique.
Vous pouvez créer une extension UIView. Dans l'exemple d'extrait ci-dessous, la fonction copyView renvoie un AnyObject afin que vous puissiez copier n'importe quelle sous-classe d'un UIView, ie UIImageView. Si vous souhaitez copier niquement UIView, vous pouvez changer le type de retour en UIView.
//MARK: - UIView Extensions
extension UIView
{
func copyView<T: UIView>() -> T {
return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T
}
}
Exemple d'utilisation:
let sourceView = UIView()
let copiedView: UIView = sourceView.copyView()
Mise à jour pour iOS 12.
Les méthodes archivedData(withRootObject:)
et unarchivedObject(with:)
sont obsolètes à partir d'iOS 12.0.
Voici une mise à jour de la réponse de @Ivan Porcolab en utilisant la nouvelle API (depuis 11.0), également rendue plus générale pour prendre en charge d'autres types.
extension NSObject {
func copyObject<T:NSObject>() throws -> T? {
let data = try NSKeyedArchiver.archivedData(withRootObject:self, requiringSecureCoding:false)
return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? T
}
}
Cette réponse montre comment faire ce que @uliwitness a suggéré. Autrement dit, obtenez un objet identique en l'archivant puis en le désarchivant. (C'est aussi essentiellement ce qu'a fait Ivan Porkolab dans sa réponse, mais dans un format plus lisible, je pense.)
let myView = UIView()
// create an NSData object from myView
let archive = NSKeyedArchiver.archivedData(withRootObject: myView)
// create a clone by unarchiving the NSData
let myViewCopy = NSKeyedUnarchiver.unarchiveObject(with: archive) as! UIView
AnyObject
. Nous avons utilisé as! UIView
pour taper le convertir en UIView
car nous savons que c'est ce que c'est. Si notre vue était un UITextView
, nous pourrions taper cast it as! UITextView
.myViewCopy
n'a plus de vue parent.UIImage
. Cependant, voir this et this answer.Mise à jour vers Swift 3.0
Je pense que vous devriez lier votre UIView avec un .nib et juste en créer un nouveau.
La propriété ne sera pas la même, mais vous conservez l'apparence et les méthodes.
Vous devez utiliser le modèle Prototype
Exemple du prototype:
class ThieveryCorporationPersonDisplay {
var name: String?
let font: String
init(font: String) {
self.font = font
}
func clone() -> ThieveryCorporationPersonDisplay {
return ThieveryCorporationPersonDisplay(font:self.font)
}
}
Un exemple d'utilisation de prototype:
let Prototype = ThieveryCorporationPersonDisplay(font:"Ubuntu")
let Philippe = Prototype.clone()
Philippe.name = "Philippe"
let Christoph = Prototype.clone()
Christoph.name = "Christoph"
let Eduardo = Prototype.clone()
Eduardo.name = "Eduardo"
Une solution supplémentaire pourrait être de simplement créer un nouveau UIView
, puis de copier toutes les propriétés critiques. Cela peut ne pas fonctionner dans le cas du PO, mais cela pourrait très bien fonctionner pour d'autres cas.
Par exemple, avec un UITextView
, vous n'aurez probablement besoin que du cadre et du texte attribué:
let textViewCopy = UITextView(frame: textView.frame)
textViewCopy.attributedText = textView.attributedText