23

I have a UIView that is linked to a UIViewController via the Interface Builder. Is it possible to duplicate, clone or copy this view so that I can use it more than once?

Thizzer
  • 16,153
  • 28
  • 98
  • 139

4 Answers4

31

The following category might not be particularly efficient, but worked for me in one project:

@implementation UIView (OPCloning)

- (id) clone {
    NSData *archivedViewData = [NSKeyedArchiver archivedDataWithRootObject: self];
    id clone = [NSKeyedUnarchiver unarchiveObjectWithData:archivedViewData];
    return clone;
}

@end

I'd not implement -copy or -copyWithZone: as Apple might do so in the future. Note, that not all views implement archiving to the same extent. You'll definitely need to implement the NSCoding methods for custom properties of your NSView subclasses to be cloned (will turn to nil in the cloned view, otherwise). Still easier than writing custom cloning code.

osxdirk
  • 560
  • 6
  • 11
  • 1
    It should be more efficient than repeatedly loading a nib for that as it does not access external storage, though. – osxdirk Dec 02 '12 at 04:04
  • In my case archiving and unarchiving together took about 20-25% more time than loading nib from the bundle(external storage). Anyway I'm still looking for way to improve loadNibNamed performance. – hris.to Jan 20 '15 at 10:56
  • Images in cloned view is loading too slow in iOS 9. – Suresh Sep 22 '15 at 05:31
  • Nib loading basically uses keyed archiving under the hood, so performance should be the same. The reason it isn't here is that a Nib has already been archived during the build, and is now being unarchived several times. This code does the archiving and unarchiving each time. You should see similar performance if you cache the archivedViewData and reuse it. But if you need to clone actual characteristics that change during the runtime of your program, a Nib should be slower. – uliwitness Feb 24 '17 at 11:49
16

Here is a new method you can use: Use UIView's method:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

This is the fastest way to draw a view. Available in iOS 7.

Lei Zhang
  • 500
  • 4
  • 15
  • 1
    Note: While this is a good solution for a very specific use case, this method does not actually create a new view of the same type as the original. It only creates a view that looks like the view looks at this moment (i.e. it "takes a screenshot" of the view), which you can use as a stand-in e.g. during transition effects or when reloading/recreating views. – uliwitness Feb 24 '17 at 11:47
5

You can try with Swift 3.0.1 below:

extension UIView{
func copyView() -> AnyObject{
    return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self))! as AnyObject
 }
}
Sour LeangChhean
  • 7,089
  • 6
  • 37
  • 39
0

Sure. The documentation has a good example of how to achieve that; it's for UITableViewCell, but is a good approach to use here as well.

Depending on the complexity of your view, you might want to make it a custom view class, and give it its own IBOutlet properties for whatever subviews it has; in that case, you'd set the “Class Identity” of the view in Interface Builder to that class. Then your view controller could access those views on any given XIB-loaded view via, for instance, myLoadedView.someLabel, rather than having to use, e.g., [myLoadedView viewWithTag:3] as suggested by the aforelinked documentation.

Noah Witherspoon
  • 57,021
  • 16
  • 130
  • 131
  • 2
    The UITableViewCell is only loaded several times, never cloned. – geon Jun 13 '11 at 13:49
  • 9
    My guess is that @geon is suggesting that, while creating a new object by unarchiving a nib multiple times seems to solve the OP's needs, it doesn't serve as a general solution to the problem as implied by the question's title. This won't work in the general case of a UIView created in code, e.g., with `[UIView alloc] initWithFrame:aFrame];`. The answer's correct, but the question title's misleading. – clozach Nov 30 '11 at 04:57