This repository has been archived by the owner. It is now read-only.

Storyboard support #8

Merged
merged 22 commits into from Jan 24, 2013
Commits
Jump to file or symbol
Failed to load files and symbols.
+430 −44
Diff settings

Always

Just for now

@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
589E4E5C16AB267E00E9E0DF /* CKParagraphStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 589E4E5B16AB267E00E9E0DF /* CKParagraphStyle.m */; };
B71610F61631C4EB00B08595 /* CKRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = B71610F51631C4EB00B08595 /* CKRefreshControl.m */; };
B71610F71631C4F400B08595 /* CKRefreshControl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B71610F41631C4EB00B08595 /* CKRefreshControl.h */; };
B71610FA1631C54E00B08595 /* CKRefreshArrowView.m in Sources */ = {isa = PBXBuildFile; fileRef = B71610F91631C54E00B08595 /* CKRefreshArrowView.m */; };
@@ -26,6 +27,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
589E4E5A16AB267E00E9E0DF /* CKParagraphStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKParagraphStyle.h; sourceTree = "<group>"; };
589E4E5B16AB267E00E9E0DF /* CKParagraphStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKParagraphStyle.m; sourceTree = "<group>"; };
B71610E31631C4BC00B08595 /* libCKRefreshControl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCKRefreshControl.a; sourceTree = BUILT_PRODUCTS_DIR; };
B71610EA1631C4BC00B08595 /* CKRefreshControl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CKRefreshControl-Prefix.pch"; sourceTree = "<group>"; };
B71610F41631C4EB00B08595 /* CKRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKRefreshControl.h; sourceTree = "<group>"; };
@@ -69,6 +72,8 @@
B71610F51631C4EB00B08595 /* CKRefreshControl.m */,
B71610F81631C54E00B08595 /* CKRefreshArrowView.h */,
B71610F91631C54E00B08595 /* CKRefreshArrowView.m */,
589E4E5A16AB267E00E9E0DF /* CKParagraphStyle.h */,
589E4E5B16AB267E00E9E0DF /* CKParagraphStyle.m */,
);
path = CKRefreshControl;
sourceTree = "<group>";
@@ -134,6 +139,7 @@
files = (
B71610F61631C4EB00B08595 /* CKRefreshControl.m in Sources */,
B71610FA1631C54E00B08595 /* CKRefreshArrowView.m in Sources */,
589E4E5C16AB267E00E9E0DF /* CKParagraphStyle.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -0,0 +1,35 @@
// CKParagraphStyle.h
//
// Copyright (c) 2012 Instructure, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
@interface CKParagraphStyle : NSObject
@end
@class NSAttributedString;
@class NSParagraphStyle;
@interface NSAttributedString (CKParagraphStyleAdditions)
// This will be added to the class at runtime if not already available
@property (nonatomic,retain) NSParagraphStyle *paragraphStyle;
@end
@@ -0,0 +1,74 @@
// CKParagraphStyle.m
//
// Copyright (c) 2012 Instructure, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "CKParagraphStyle.h"
#import <objc/objc-runtime.h>
static BOOL _isMasquerading = NO;
@implementation CKParagraphStyle
#if __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_5_1
#define IMP_WITH_BLOCK_TYPE __bridge void*
#else
#define IMP_WITH_BLOCK_TYPE id
#endif
static void *NSParagraphStyleKey;
- (id) initWithCoder: (NSCoder *) aDecoder
{
return [super init];
}
+ (void) load
{
if (objc_getClass("NSParagraphStyle"))
return;
// CKParagraphStyle will masquerade as NSParagraphStyle
static dispatch_once_t registerNSParagraphStyleClass_onceToken;
dispatch_once(&registerNSParagraphStyleClass_onceToken, ^{
Class nsParagraphStyleClass = objc_allocateClassPair([self class], "NSParagraphStyle", 0);
objc_registerClassPair(nsParagraphStyleClass);
_isMasquerading = YES;
Class nsAttributedStringClass = [NSAttributedString class];
IMP paragraphStyleIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^NSParagraphStyle *(id dynamicSelf) {
return objc_getAssociatedObject(dynamicSelf, &NSParagraphStyleKey);
}));
BOOL added = class_addMethod(nsAttributedStringClass, @selector(paragraphStyle), paragraphStyleIMP, "@@:");
NSAssert(added, @"We tried to add the paragraphStyle method, and it failed. This is going to break things, so we may as well stop here.");
IMP setParagraphStyleIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^void(NSAttributedString *dynamicSelf, id paragraphStyle) {
if (dynamicSelf.paragraphStyle == paragraphStyle) {
return;
}
objc_setAssociatedObject(dynamicSelf, &NSParagraphStyleKey, paragraphStyle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}));
added = class_addMethod(nsAttributedStringClass, @selector(setParagraphStyle:), setParagraphStyleIMP, "v@:@");
NSAssert(added, @"We tried to add the setParagraphStyle: method, and it failed. This is going to break things, so we may as well stop here.");
});
}
@end
@@ -22,6 +22,7 @@
#import "CKRefreshControl.h"
#import "CKRefreshArrowView.h"
#import "CKParagraphStyle.h"
#import <objc/runtime.h>
#if !__has_feature(objc_arc)
@@ -35,6 +36,7 @@
CKRefreshControlStateRefreshing
} CKRefreshControlState;
static BOOL _isMasquerading = NO;
@interface CKRefreshControl ()
@property (nonatomic) CKRefreshControlState refreshControlState;
@@ -51,22 +53,58 @@ @implementation CKRefreshControl {
- (id)init
{
Class uiRefreshControlClass = NSClassFromString(@"UIRefreshControl");
if (uiRefreshControlClass) {
return [[uiRefreshControlClass alloc] init];
}
CGRect defaultFrame = CGRectMake(0, 0, 320, 60);
self = [super initWithFrame:defaultFrame];
if (self) {
// Initialization code
if (uiRefreshControlClass && !_isMasquerading)
return [[uiRefreshControlClass alloc] init];
if (self = [super init])
{
[self commonInit];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
self.backgroundColor = [UIColor clearColor];
[self populateSubviews];
[self setRefreshControlState:CKRefreshControlStateHidden];
}
return self;
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self commonInit];
if ([aDecoder containsValueForKey:@"UITintColor"])
self.tintColor = (UIColor *)[aDecoder decodeObjectForKey:@"UITintColor"];
if ([aDecoder containsValueForKey:@"UIAttributedTitle"])
self.attributedTitle = [aDecoder decodeObjectForKey:@"UIAttributedTitle"];
// we can set its refresh control when the table view controller sets its view
[[NSNotificationCenter defaultCenter] addObserverForName: CKRefreshControl_UITableViewController_DidSetView_Notification
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
UITableViewController *tableViewController = notification.object;
if (tableViewController.refreshControl != (id)self)
tableViewController.refreshControl = (id)self;
} ];
}
return self;
}
- (void) commonInit
{
self.frame = CGRectMake(0, 0, 320, 60);
[self populateSubviews];
[self setRefreshControlState:CKRefreshControlStateHidden];
}
// remove notification observer in case notification never fired
- (void) awakeFromNib
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:CKRefreshControl_UITableViewController_DidSetView_Notification object:nil];
[super awakeFromNib];
}
- (void)populateSubviews {
CGRect frame = CGRectInset(self.bounds, 12, 12);
arrow = [[CKRefreshArrowView alloc] initWithFrame:frame];
@@ -220,7 +258,8 @@ - (void)didMoveToSuperview {
if ([superview isKindOfClass:[UITableView class]]) {
UITableViewController *tableViewController = (UITableViewController *)superview.nextResponder;
if ([tableViewController isKindOfClass:[UITableViewController class]]) {
tableViewController.refreshControl = (id)self;
if (tableViewController.refreshControl != (id)self)
tableViewController.refreshControl = (id)self;
}
}
}
@@ -357,12 +396,11 @@ + (id)appearanceWhenContainedIn:(Class<UIAppearanceContainer>)ContainerClass, ..
// will work on both iOS 5 and iOS 6.
+ (Class)class {
Class uiRefreshControlClass = NSClassFromString(@"UIRefreshControl");
if (uiRefreshControlClass) {
if (uiRefreshControlClass)
return uiRefreshControlClass;
}
else {
return [super class];
}
return [super class];
}
@@ -372,38 +410,82 @@ + (Class)class {
#define IMP_WITH_BLOCK_TYPE id
#endif
typedef IMP *IMPPointer;
static void *CKRefreshControlKey;
+ (void)load {
if ([UITableViewController instancesRespondToSelector:@selector(refreshControl)]) {
return;
}
// Add UITableViewController.refreshControl if it isn't present
Class tableViewController = [UITableViewController class];
IMP refreshControlIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^id(id dynamicSelf) {
return objc_getAssociatedObject(dynamicSelf, &CKRefreshControlKey);
}));
BOOL added = class_addMethod(tableViewController, @selector(refreshControl), refreshControlIMP, "@@:");
NSAssert(added, @"We tried to add the refreshControl method, and it failed. This is going to break things, so we may as well stop here.");
static BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store)
{
IMP imp = NULL;
Method method = class_getInstanceMethod(class, original);
IMP setRefreshControlIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^void(UITableViewController *dynamicSelf, id newControl) {
if (dynamicSelf.refreshControl == newControl) {
return;
}
objc_setAssociatedObject(dynamicSelf, &CKRefreshControlKey, newControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (method)
{
const char *type = method_getTypeEncoding(method);
imp = class_replaceMethod(class, original, replacement, type);
if ([newControl superview] != dynamicSelf.tableView) {
[dynamicSelf.tableView addSubview:newControl];
}
}));
if (!imp)
imp = method_getImplementation(method);
}
if (imp && store)
*store = imp;
added = class_addMethod(tableViewController, @selector(setRefreshControl:), setRefreshControlIMP, "v@:@");
NSAssert(added, @"We tried to add the setRefreshControl: method, and it failed. This is going to break things, so we may as well stop here.");
return (imp != NULL);
}
NSString *const CKRefreshControl_UITableViewController_DidSetView_Notification = @"CKRefreshControl_UITableViewController_DidSetView";
static void CKRefreshControl_UITableViewController_SetView(UITableViewController *dynamicSelf, SEL _cmd, UITableView *view);
static void (*UITableViewController_SetViewIMP)(UITableViewController *dynamicSelf, SEL _cmd, UITableView *view);
static void CKRefreshControl_UITableViewController_SetView(UITableViewController *dynamicSelf, SEL _cmd, UITableView *view)
{
UITableViewController_SetViewIMP(dynamicSelf, _cmd, view);
[[NSNotificationCenter defaultCenter] postNotificationName:CKRefreshControl_UITableViewController_DidSetView_Notification object:dynamicSelf];
}
+ (void)load
{
if (objc_getClass("UIRefreshControl"))
return;
@end
// CKRefreshControl will masquerade as UIRefreshControl
static dispatch_once_t registerUIRefreshControlClass_onceToken;
dispatch_once(&registerUIRefreshControlClass_onceToken, ^{
Class uiRefreshControlClass = objc_allocateClassPair([self class], "UIRefreshControl", 0);
objc_registerClassPair(uiRefreshControlClass);
_isMasquerading = YES;
// Add UITableViewController.refreshControl if it isn't present
Class tableViewController = [UITableViewController class];
IMP refreshControlIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^id(id dynamicSelf) {
return objc_getAssociatedObject(dynamicSelf, &CKRefreshControlKey);
}));
BOOL added = class_addMethod(tableViewController, @selector(refreshControl), refreshControlIMP, "@@:");
NSAssert(added, @"We tried to add the refreshControl method, and it failed. This is going to break things, so we may as well stop here.");
IMP setRefreshControlIMP = imp_implementationWithBlock((IMP_WITH_BLOCK_TYPE)(^void(UITableViewController *dynamicSelf, id newControl) {
if (dynamicSelf.refreshControl == newControl) {
return;
}
objc_setAssociatedObject(dynamicSelf, &CKRefreshControlKey, newControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([newControl superview] != dynamicSelf.tableView) {
[dynamicSelf.tableView addSubview:newControl];
}
}));
added = class_addMethod(tableViewController, @selector(setRefreshControl:), setRefreshControlIMP, "v@:@");
NSAssert(added, @"We tried to add the setRefreshControl: method, and it failed. This is going to break things, so we may as well stop here.");
// swizzle setView: method so we can post notification to CKRefreshControl ...
// CKRefreshControl will assign itself to the UIViewController
class_swizzleMethodAndStore(tableViewController,
@selector(setView:),
(IMP)CKRefreshControl_UITableViewController_SetView,
(IMP *)&UITableViewController_SetViewIMP );
});
}
@end
Oops, something went wrong.