Skip to content

Commit

Permalink
允许在桥接的view.xib中指定file‘s owner (merge from rush)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnyxx committed Sep 10, 2014
1 parent e3de340 commit e48901e
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 4 deletions.
6 changes: 5 additions & 1 deletion XXNibBridge/XXNibBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
+ (UINib *)xx_nib;

/// Load object of this class from IB file with SAME name
+ (id)xx_loadFromNib;
+ (id)xx_loadFromNibWithOwner:(id)owner;

/// Load UIViewController of this class from given storyboard name
+ (id/*UIViewController*/)xx_loadFromStoryboardNamed:(NSString *)name;
Expand All @@ -31,4 +31,8 @@
/// default -> NO
+ (BOOL)xx_shouldApplyNibBridging;

+ (Class)xx_ownerClass;

- (id)owner;

@end
24 changes: 21 additions & 3 deletions XXNibBridge/XXNibBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

#import "XXNibBridge.h"
#import <objc/runtime.h>

@implementation NSObject (XXNibLoading)

Expand All @@ -14,9 +15,9 @@ + (NSString *)xx_nibID
return NSStringFromClass(self);
}

+ (id)xx_loadFromNib
+ (id)xx_loadFromNibWithOwner:(id)owner
{
NSArray *objects = [[self xx_nib] instantiateWithOwner:nil options:nil];
NSArray *objects = [[self xx_nib] instantiateWithOwner:owner options:nil];
for (UIView *obj in objects)
{
if ([obj isMemberOfClass:self])
Expand Down Expand Up @@ -72,6 +73,16 @@ + (BOOL)xx_shouldApplyNibBridging
return NO;
}

+ (Class)xx_ownerClass
{
return nil;
}

- (id)owner
{
return objc_getAssociatedObject(self, "owner");
}

- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
self = [super awakeAfterUsingCoder:aDecoder];
Expand All @@ -87,8 +98,15 @@ - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
setIBReplaceFlag([self class], YES);

Class ownerClass = [[self class] xx_ownerClass];
id owner;
if (ownerClass) {
owner = [ownerClass new];
}
// Require nib name is equal to class name
UIView *view = [[self class] xx_loadFromNib];
UIView *view = [[self class] xx_loadFromNibWithOwner:owner];

objc_setAssociatedObject(view, "owner", owner, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

NSAssert(view, @"View of class [%@] could not load from nib, check whether the view in nib binds the correct class", [[self class] xx_nibID]);

Expand Down
14 changes: 14 additions & 0 deletions XXNibBridgeDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
48107E561965BBEF002C7C38 /* XXDogeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 48107E511965BBEF002C7C38 /* XXDogeView.xib */; };
48107E571965BBEF002C7C38 /* XXSarkView.m in Sources */ = {isa = PBXBuildFile; fileRef = 48107E531965BBEF002C7C38 /* XXSarkView.m */; };
48107E581965BBEF002C7C38 /* XXSarkView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 48107E541965BBEF002C7C38 /* XXSarkView.xib */; };
48F0CFBB19C04D9000774D54 /* XXNibBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 48F0CFBA19C04D9000774D54 /* XXNibBridge.m */; };
604CF887893E4B918C3F7455 /* libPods-XXNibBridgeDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B50C815C20446C6B7399B9D /* libPods-XXNibBridgeDemo.a */; };
/* End PBXBuildFile section */

Expand All @@ -43,6 +44,8 @@
48107E521965BBEF002C7C38 /* XXSarkView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XXSarkView.h; sourceTree = "<group>"; };
48107E531965BBEF002C7C38 /* XXSarkView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XXSarkView.m; sourceTree = "<group>"; };
48107E541965BBEF002C7C38 /* XXSarkView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = XXSarkView.xib; sourceTree = "<group>"; };
48F0CFB919C04D9000774D54 /* XXNibBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XXNibBridge.h; sourceTree = "<group>"; };
48F0CFBA19C04D9000774D54 /* XXNibBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XXNibBridge.m; sourceTree = "<group>"; };
CB00ED07703C43B889CF8245 /* Pods-XXNibBridgeDemo.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XXNibBridgeDemo.xcconfig"; path = "Pods/Pods-XXNibBridgeDemo.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -94,6 +97,7 @@
48107E1C1965BAA9002C7C38 /* XXNibBridgeDemo */ = {
isa = PBXGroup;
children = (
48F0CFB819C04D9000774D54 /* XXNibBridge */,
48107E591965BBF2002C7C38 /* Nib views */,
48107E251965BAA9002C7C38 /* XXAppDelegate.h */,
48107E261965BAA9002C7C38 /* XXAppDelegate.m */,
Expand Down Expand Up @@ -128,6 +132,15 @@
name = "Nib views";
sourceTree = "<group>";
};
48F0CFB819C04D9000774D54 /* XXNibBridge */ = {
isa = PBXGroup;
children = (
48F0CFB919C04D9000774D54 /* XXNibBridge.h */,
48F0CFBA19C04D9000774D54 /* XXNibBridge.m */,
);
path = XXNibBridge;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -234,6 +247,7 @@
48107E571965BBEF002C7C38 /* XXSarkView.m in Sources */,
48107E271965BAA9002C7C38 /* XXAppDelegate.m in Sources */,
48107E231965BAA9002C7C38 /* main.m in Sources */,
48F0CFBB19C04D9000774D54 /* XXNibBridge.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
39 changes: 39 additions & 0 deletions XXNibBridgeDemo/XXNibBridge/XXNibBridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// XXNibBridge.h
//
// Created by sunnyxx on 14-7-2.
// Copyright (c) 2014 sunnyxx. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSObject (XXIBLoadingConvenient)

/// For convinent, using class name for identifier in IB.
/// etc cell reuse id, storyboard id
+ (NSString *)xx_nibID;

/// UINib object with same name from main bundle
+ (UINib *)xx_nib;

/// Load object of this class from IB file with SAME name
+ (id)xx_loadFromNibWithOwner:(id)owner;
+ (id)xx_loadFromNib; // Nil owner

/// Load UIViewController of this class from given storyboard name
+ (id/*UIViewController*/)xx_loadFromStoryboardNamed:(NSString *)name;

@end


@interface UIView (XXNibBridge)

/// Subclass override it to switch On/Off IB bridging.
/// default -> NO
+ (BOOL)xx_shouldApplyNibBridging;

+ (Class)xx_ownerClass;

- (id)owner;

@end
166 changes: 166 additions & 0 deletions XXNibBridgeDemo/XXNibBridge/XXNibBridge.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//
// XXNibBridge.m
//
// Created by sunnyxx on 14-7-2.
// Copyright (c) 2014 sunnyxx. All rights reserved.
//

#import "XXNibBridge.h"

@implementation NSObject (XXNibLoading)

+ (NSString *)xx_nibID
{
return NSStringFromClass(self);
}

+ (id)xx_loadFromNibWithOwner:(id)owner
{
NSArray *objects = [[self xx_nib] instantiateWithOwner:owner options:nil];
for (UIView *obj in objects)
{
if ([obj isMemberOfClass:self])
{
return obj;
}
}
return nil;
}

+ (id)xx_loadFromNib
{
return [self xx_loadFromNibWithOwner:nil];
}

+ (id)xx_loadFromStoryboardNamed:(NSString *)name
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:name bundle:nil];
return [sb instantiateViewControllerWithIdentifier:[self xx_nibID]];
}

+ (UINib *)xx_nib
{
return [UINib nibWithNibName:[self xx_nibID] bundle:nil];
}

@end

// Mapping flags that whether a class has been replaced

static NSMutableDictionary * getIBReplaceFlagMapping()
{
static NSMutableDictionary *mapping = nil;
if (!mapping)
{
mapping = [NSMutableDictionary dictionary];
}
return mapping;
}

static BOOL getIBReplaceFlag(Class cls)
{
NSMutableDictionary *mapping = getIBReplaceFlagMapping();
BOOL flag = [mapping[NSStringFromClass(cls)] boolValue];
return flag;
}

static void setIBReplaceFlag(Class cls, BOOL flag)
{
NSMutableDictionary *mapping = getIBReplaceFlagMapping();
mapping[NSStringFromClass(cls)] = @(flag);
}


@import ObjectiveC;

char *const kXXNibOwnerKey = "owner";

@implementation UIView (XXNibBridge)

+ (BOOL)xx_shouldApplyNibBridging
{
return NO;
}

+ (Class)xx_ownerClass
{
return nil;
}

- (id)owner
{
return objc_getAssociatedObject(self, kXXNibOwnerKey);
}

- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
self = [super awakeAfterUsingCoder:aDecoder];

if (![[self class] xx_shouldApplyNibBridging])
{
return self;
}

// self will be replaced by object created from nib

if (!getIBReplaceFlag([self class]))
{
setIBReplaceFlag([self class], YES);

Class ownerClass = [[self class] xx_ownerClass];
id owner;
if (ownerClass) {
owner = [[ownerClass alloc] initWithCoder:aDecoder];
}
// Require nib name is equal to class name
UIView *view = [[self class] xx_loadFromNibWithOwner:owner];

objc_setAssociatedObject(view, kXXNibOwnerKey, owner, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

NSAssert(view, @"View of class [%@] could not load from nib, check whether the view in nib binds the correct class", [[self class] xx_nibID]);

view.frame = self.frame;
view.autoresizingMask = self.autoresizingMask;
view.hidden = self.hidden;

// Autolayout support
if ([view respondsToSelector:@selector(translatesAutoresizingMaskIntoConstraints)])
{
view.translatesAutoresizingMaskIntoConstraints = NO;
}

// Autolayout constrains replacing
// Replace all constrains from `placeholder(self)` view to `real` view
if ([view respondsToSelector:@selector(constraints)] && self.constraints.count > 0)
{
[self replaceAutolayoutConstrainsFromView:self toView:view];
}

return view;
}

// Reset flag
setIBReplaceFlag([self class], NO);

return self;
}

- (void)replaceAutolayoutConstrainsFromView:(UIView *)placeholderView toView:(UIView *)realView
{
// We only need to copy `self` constraints (like width/height constraints)
// from placeholder to real view
for (NSLayoutConstraint *constraint in placeholderView.constraints)
{
NSLayoutConstraint* newConstraint = [NSLayoutConstraint constraintWithItem:realView
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:nil // Only first item
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant];
newConstraint.shouldBeArchived = constraint.shouldBeArchived;
newConstraint.priority = constraint.priority;
[realView addConstraint:newConstraint];
}
}
@end

0 comments on commit e48901e

Please sign in to comment.