可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Because objects are reference types, not value types, if you set a UIView
equal to another UIView
, the views are the same object. If you modify one you'll modifying the other as well.
I have an interesting situation where I would like to add a UIView
as a subview in another view, then I make some modifications, and those modifications should not affect the original UIView
. How can I make a copy of the UIView
so I can ensure I add that copy as a subview instead of a reference to the original UIView
?
Note that I can't recreate the view in the same way the original was created, I need some way to create a copy given any UIView
object.
回答1:
You can't arbitrarily copy an object. Only objects that implement the NSCopying
protocol can be copied.
However, there is a workaround: Since UIView
s can be serialized to disk (e.g. to load from a XIB), you could use NSKeyedArchiver
and NSKeyedUnarchiver
to create a serialized NSData
describing your view, then de-serialize that again to get an independent but identical object.
回答2:
You can make an UIView extension. In example snippet below, function copyView returns an AnyObject so you could copy any subclass of an UIView, ie UIImageView. If you want to copy only UIView you can change the return type to UIView.
//MARK: - UIView Extensions
extension UIView
{
func copyView<T: UIView>() -> T {
return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T
}
}
Example usage:
let sourceView = UIView()
let copiedView: UIView = sourceView.copyView()
回答3:
This answer shows how to do what @uliwitness suggested. That is, get an identical object by archiving it and then unarchiving it. (It is also basically what Ivan Porkolab did in his answer, but in a more readable format, I think.)
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
Notes
- Unarchiving the data creates an object of type
AnyObject
. We used as! UIView
to type cast it back to a UIView
since we know that's what it is. If our view were a UITextView
then we could type cast it as! UITextView
.
- The
myViewCopy
no longer has a parent view.
- Some people mention some problems when working with
UIImage
. However, see this and this answer.
Updated to Swift 3.0
回答4:
I think that you should link you UIView with a .nib and just create a new one.
Property will not be the same, but you keep appearance and methods.
回答5:
You must use pattern Prototype
Example of the prototype:
class ThieveryCorporationPersonDisplay {
var name: String?
let font: String
init(font: String) {
self.font = font
}
func clone() -> ThieveryCorporationPersonDisplay {
return ThieveryCorporationPersonDisplay(font:self.font)
}
}
An example of using 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"
回答6:
Update for iOS 12.0
Methods archivedData(withRootObject:)
and unarchivedObject(with:)
are deprecated as of iOS 12.0.
Here is an update to @Ivan Porcolab's answer using the newer API (since 11.0), also made more general to support other 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
}
}
回答7:
An addition solution could be to just create a new UIView
and then copy over any critical properties. This may not work in the OP's case, but it could very well work for other cases.
For example, with a UITextView
, probably all you would need is the frame and attributed text:
let textViewCopy = UITextView(frame: textView.frame)
textViewCopy.attributedText = textView.attributedText
回答8:
Additionally you can use this pattern to copy View controller view
let vc = UIViewController()
let anotherVc = UIViewController()
vc.view = anotherVc.copyView()
You may need this for caching view controller or cloning.