2014-11-25 10 views
20

Poiché gli oggetti sono tipi di riferimento, non tipi di valore, se si imposta un UIView uguale a un altro UIView, le viste sono lo stesso oggetto. Se ne modifichi uno modificherà anche l'altro.Creare una copia di un UIView in Swift

Ho una situazione interessante in cui vorrei aggiungere un UIView come sottoview in un'altra vista, quindi apporto alcune modifiche e tali modifiche non dovrebbero influire sull'originale UIView. Come posso fare una copia dello UIView così posso assicurarmi di aggiungere quella copia come sottoview invece di un riferimento all'originale UIView?

Nota che non riesco a ricreare la vista nello stesso modo in cui è stato creato l'originale, ho bisogno di un modo per creare una copia dato qualsiasi oggetto UIView.

risposta

10

Non è possibile copiare arbitrariamente un oggetto. Solo gli oggetti che implementano il protocollo NSCopying possono essere copiati.

Tuttavia, c'è una soluzione: Dal UIView s può essere serializzato su disco (ad esempio, per caricare da un XIB), è possibile utilizzare NSKeyedArchiver e NSKeyedUnarchiver per creare un serializzato NSData descrive la vostra vista, poi de-serializzare che ancora una volta a ottenere un oggetto indipendente ma identico.

+0

@DannyBravo dove si ottiene che dal? Il mio UIView.h dichiara solo la conformità a NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem e CALayerDelegate. Nessun NSCopying in là, per quanto posso vedere. – uliwitness

+0

Hai perfettamente ragione, mi sono confuso per un po '. Sto rimuovendo la mia risposta. –

3

Penso che dovresti collegare UIView con un .nib e crearne uno nuovo.

La proprietà non sarà la stessa, ma si mantengono l'aspetto e i metodi.

+0

Anche questo è un buon approccio. Se hai già un XIB in cui definisci l'oggetto, e sei principalmente preoccupato di configurarlo correttamente, e gli attributi che cambi su di esso sono pochi, puoi semplicemente caricare l'XIB una seconda volta (puoi avere XIBs con una singola vista fluttuante in loro). – uliwitness

+0

se basato su xib. – eric

1

È necessario utilizzare modello Prototype

Esempio del prototipo:

class ThieveryCorporationPersonDisplay { 
    var name: String? 
    let font: String 

    init(font: String) { 
     self.font = font 
    } 

    func clone() -> ThieveryCorporationPersonDisplay { 
     return ThieveryCorporationPersonDisplay(font:self.font) 
    } 
} 

Un esempio di utilizzo del prototipo:

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" 
60

di poter effettuare un'estensione UIView. Nello snippet di esempio seguente, la funzione copyView restituisce un oggetto AnyObject in modo da poter copiare qualsiasi sottoclasse di un UIView, ovvero UIImageView. Se vuoi copiare solo UIView puoi cambiare il tipo di ritorno in UIView.

//MARK: - UIView Extensions 

extension UIView 
{ 
    func copyView<T: UIView>() -> T { 
     return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T 
    } 
} 

Esempio di utilizzo:

let sourceView = UIView() 
let copiedView: UIView = sourceView.copyView() 
+2

Questa dovrebbe essere la risposta accettata –

+1

In che modo questo non è un metodo factory su UIView? –

+0

Molto utile! Grazie mille! – Glenn

2

Questa risposta dimostra come fare ciò che @uliwitness suggerito. Cioè, ottenere un oggetto identico archiviandolo e quindi annullarlo. (E 'anche fondamentalmente ciò che Ivan Porkolab ha fatto nella sua risposta, ma in un formato più leggibile, credo.)

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 

Note

  • Annullamento dell'archiviazione dei dati crea un oggetto di tipo AnyObject. Abbiamo utilizzato as! UIView per digitare di nuovo il cast in un UIView poiché sappiamo che è quello che è. Se il nostro punto di vista fosse un UITextView, potremmo digitare castarlo as! UITextView.
  • Il myViewCopy non ha più una vista principale.
  • Some people menzionare alcuni problemi quando si lavora con UIImage. Tuttavia, vedere la risposta this e this.

Aggiornato a Swift 3,0

+0

Ho implementato questo e per un secondo ho pensato che funzionasse. Ma sto copiando un UIView che ha sottoview e il risultato che sto ottenendo sono le sotto-visualizzazioni, ma non la vista stessa. Ho visto il debug della gerarchia delle viste. La vista che im copying è un IBOutlet invece di 'UIView()'. Sembra familiare? –

+0

Mi dispiace. Quando eseguo il debug di un po 'di più, ciò che accade è che nella vista copiata ho 'view.top = view.bottom' come un vincolo ed è per questo che non lo vedo nella gerarchia della vista. –

0

Una soluzione aggiunta potrebbe essere quella di creare solo un nuovo UIView e quindi copiare su qualsiasi proprietà critiche. Questo potrebbe non funzionare nel caso dell'OP, ma potrebbe benissimo funzionare per altri casi.

Ad esempio, con un UITextView, probabilmente tutto quello che vi serve è la cornice e il testo attribuito:

let textViewCopy = UITextView(frame: textView.frame) 
textViewCopy.attributedText = textView.attributedText 
Problemi correlati