-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathUIView+NibLoading.m
160 lines (136 loc) · 5.64 KB
/
UIView+NibLoading.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//
// UIView+NibLoading.m
//
// Created by Nicolas Bouilleaud.
//
// https://github.com/n-b/UIView-NibLoading
#import "UIView+NibLoading.h"
#import <objc/runtime.h>
@implementation UIView(NibLoading)
+ (UINib*) _nibLoadingAssociatedNibWithName:(NSString*)nibName
{
static char kUIViewNibLoading_associatedNibsKey;
NSDictionary * associatedNibs = objc_getAssociatedObject(self, &kUIViewNibLoading_associatedNibsKey);
UINib * nib = associatedNibs[nibName];
if(nil==nib)
{
nib = [UINib nibWithNibName:nibName bundle:[NSBundle bundleForClass:self]];
if(nib)
{
NSMutableDictionary * newNibs = [NSMutableDictionary dictionaryWithDictionary:associatedNibs];
newNibs[nibName] = nib;
objc_setAssociatedObject(self, &kUIViewNibLoading_associatedNibsKey, [NSDictionary dictionaryWithDictionary:newNibs], OBJC_ASSOCIATION_RETAIN);
}
}
return nib;
}
static char kUIViewNibLoading_outletsKey;
- (void) loadContentsFromNibNamed:(NSString*)nibName
{
// Load the nib file, setting self as the owner.
// The root view is only a container and is discarded after loading.
UINib * nib = [[self class] _nibLoadingAssociatedNibWithName:nibName];
NSAssert(nib!=nil, @"UIView+NibLoading : Can't load nib named %@.",nibName);
// Instantiate (and keep a list of the outlets set through KVC.)
NSMutableDictionary * outlets = [NSMutableDictionary new];
objc_setAssociatedObject(self, &kUIViewNibLoading_outletsKey, outlets, OBJC_ASSOCIATION_RETAIN);
NSArray * views = [nib instantiateWithOwner:self options:nil];
NSAssert(views!=nil, @"UIView+NibLoading : Can't instantiate nib named %@.",nibName);
objc_setAssociatedObject(self, &kUIViewNibLoading_outletsKey, nil, OBJC_ASSOCIATION_RETAIN);
// Search for the first encountered UIView base object
UIView * containerView = nil;
for (id v in views)
{
if ([v isKindOfClass:[UIView class]])
{
containerView = v;
break;
}
}
NSAssert(containerView!=nil, @"UIView+NibLoading : There is no container UIView found at the root of nib %@.",nibName);
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
if(CGRectEqualToRect(self.bounds, CGRectZero))
{
// `self` has no size : use the containerView's size, from the nib file
self.bounds = containerView.bounds;
}
else
{
// `self` has a specific size : resize the containerView to this size, so that the subviews are autoresized.
containerView.bounds = self.bounds;
}
// Save constraints for later
NSArray * constraints = containerView.constraints;
// reparent the subviews from the nib file
for (UIView * view in containerView.subviews)
{
[self addSubview:view];
}
// Recreate constraints, replace containerView with self
for (NSLayoutConstraint *oldConstraint in constraints)
{
id firstItem = oldConstraint.firstItem;
id secondItem = oldConstraint.secondItem;
if (firstItem == containerView)
{
firstItem = self;
}
if (secondItem == containerView)
{
secondItem = self;
}
NSLayoutConstraint * newConstraint = [NSLayoutConstraint constraintWithItem:firstItem
attribute:oldConstraint.firstAttribute
relatedBy:oldConstraint.relation
toItem:secondItem
attribute:oldConstraint.secondAttribute
multiplier:oldConstraint.multiplier
constant:oldConstraint.constant];
[self addConstraint:newConstraint];
// If there was outlet(s) to the old constraint, replace it with the new constraint.
for (NSString * key in outlets)
{
if (outlets[key]==oldConstraint)
{
NSAssert([self valueForKey:key]==oldConstraint, @"UIView+NibLoading : Unexpected value for outlet %@ of view %@. Expected %@, found %@.", key, self, oldConstraint, [self valueForKey:key]);
[self setValue:newConstraint forKey:key];
}
}
}
}
- (void) setValue:(id)value forKey:(NSString *)key
{
// Keep a list of the outlets set during nib loading.
// (See above: This associated object only exists during nib-loading)
NSMutableDictionary * outlets = objc_getAssociatedObject(self, &kUIViewNibLoading_outletsKey);
outlets[key] = value;
[super setValue:value forKey:key];
}
- (void) loadContentsFromNib
{
NSString *className = NSStringFromClass([self class]);
// A Swift class name will be in the format of ModuleName.ClassName
// We want to remove the module name so the Nib can have exactly the same file name as the class
NSRange range = [className rangeOfString:@"."];
if (range.location != NSNotFound)
{
className = [className substringFromIndex:range.location + range.length];
}
[self loadContentsFromNibNamed:className];
}
@end
#pragma mark NibLoadedView
@implementation NibLoadedView : UIView
- (id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
[self loadContentsFromNib];
return self;
}
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
[self loadContentsFromNib];
return self;
}
@end