├── .gitignore ├── CHLayout.h ├── CHLayoutConstraint.h ├── CHLayoutConstraint.m ├── CHLayoutManager-Info.plist ├── CHLayoutManager.h ├── CHLayoutManager.m ├── CHLayoutManager.xcodeproj └── project.pbxproj ├── CHLayoutManagerAppDelegate.h ├── CHLayoutManagerAppDelegate.m ├── CHLayoutManager_Prefix.pch ├── English.lproj ├── InfoPlist.strings └── MainMenu.xib ├── LICENSE ├── NSNumber+CHLayout.h ├── NSNumber+CHLayout.m ├── NSView+CHLayout.h ├── NSView+CHLayout.m ├── README.markdown ├── SinTransformer.h ├── SinTransformer.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | **/*.mode1v3 3 | **/*.perspectivev3 4 | **/*.pbxuser 5 | **/*.xcworkspace 6 | **/xcuserdata -------------------------------------------------------------------------------- /CHLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayout.h 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import "NSView+CHLayout.h" 27 | #import "NSNumber+CHLayout.h" 28 | #import "CHLayoutManager.h" 29 | #import "CHLayoutConstraint.h" -------------------------------------------------------------------------------- /CHLayoutConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayoutConstraint.h 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import 27 | 28 | typedef enum { 29 | CHLayoutConstraintAttributeMinY = 1, //the left edge 30 | CHLayoutConstraintAttributeMaxY = 2, //the right edge 31 | CHLayoutConstraintAttributeMinX = 3, //the bottom edge 32 | CHLayoutConstraintAttributeMaxX = 4, //the top edge 33 | CHLayoutConstraintAttributeWidth = 5, //the width 34 | CHLayoutConstraintAttributeHeight = 6, //the height 35 | CHLayoutConstraintAttributeMidY = 7, //the vertical center 36 | CHLayoutConstraintAttributeMidX = 8, //the horizontal center 37 | 38 | CHLayoutConstraintAttributeMinXMinY = 101, 39 | CHLayoutConstraintAttributeMinXMidY = 102, 40 | CHLayoutConstraintAttributeMinXMaxY = 103, 41 | 42 | CHLayoutConstraintAttributeMidXMinY = 104, 43 | CHLayoutConstraintAttributeMidXMidY = 105, 44 | CHLayoutConstraintAttributeMidXMaxY = 106, 45 | 46 | CHLayoutConstraintAttributeMaxXMinY = 107, 47 | CHLayoutConstraintAttributeMaxXMidY = 108, 48 | CHLayoutConstraintAttributeMaxXMaxY = 109, 49 | 50 | CHLayoutConstraintAttributeBoundsCenter = 110, 51 | 52 | CHLayoutConstraintAttributeFrame = 1000, 53 | CHLayoutConstraintAttributeBounds = 1001 54 | } CHLayoutConstraintAttribute; 55 | 56 | #if NS_BLOCKS_AVAILABLE 57 | typedef CGFloat (^CHLayoutTransformer)(CGFloat); 58 | #endif 59 | 60 | @interface CHLayoutConstraint : NSObject { 61 | CHLayoutConstraintAttribute attribute; 62 | 63 | NSString * sourceName; 64 | CHLayoutConstraintAttribute sourceAttribute; 65 | 66 | NSValueTransformer * valueTransformer; 67 | } 68 | 69 | @property (readonly) CHLayoutConstraintAttribute attribute; 70 | @property (readonly) CHLayoutConstraintAttribute sourceAttribute; 71 | @property (readonly) NSString * sourceName; 72 | 73 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr; 74 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr offset:(CGFloat)offset; 75 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr scale:(CGFloat)scale offset:(CGFloat)offset; 76 | 77 | #if NS_BLOCKS_AVAILABLE 78 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr blockTransformer:(CHLayoutTransformer)transformer; 79 | #endif 80 | 81 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr valueTransformer:(NSValueTransformer *)transformer; 82 | - (id) initWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr valueTransformer:(NSValueTransformer *)transformer; 83 | 84 | - (CGFloat) transformValue:(CGFloat)original; 85 | - (void) applyToTargetView:(NSView *)target; 86 | - (void) applyToTargetView:(NSView *)target sourceView:(NSView *)source; 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /CHLayoutConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayoutConstraint.m 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import "CHLayout.h" 27 | 28 | CGFloat CHLayoutExtractCGFloat(id value) { 29 | CGFloat extracted = 0; 30 | if ([value respondsToSelector:@selector(CGFloatValue_ch)]) { 31 | extracted = [value CGFloatValue_ch]; 32 | } else if (CGFLOAT_IS_DOUBLE && [value respondsToSelector:@selector(doubleValue)]) { 33 | extracted = [value doubleValue]; 34 | } else if (!CGFLOAT_IS_DOUBLE && [value respondsToSelector:@selector(floatValue)]) { 35 | extracted = [value floatValue]; 36 | } 37 | return extracted; 38 | } 39 | 40 | #pragma mark Value Transformers 41 | 42 | @interface CHLayoutValueTransformer : NSValueTransformer 43 | { 44 | CGFloat offset; 45 | CGFloat scale; 46 | } 47 | 48 | + (id) transformerWithOffset:(CGFloat)anOffset scale:(CGFloat)aScale; 49 | - (id) initWithOffset:(CGFloat)anOffset scale:(CGFloat)aScale; 50 | 51 | @end 52 | 53 | @implementation CHLayoutValueTransformer 54 | 55 | + (id) transformerWithOffset:(CGFloat)anOffset scale:(CGFloat)aScale { 56 | return [[[self alloc] initWithOffset:anOffset scale:aScale] autorelease]; 57 | } 58 | 59 | - (id) initWithOffset:(CGFloat)anOffset scale:(CGFloat)aScale { 60 | self = [super init]; 61 | if (self) { 62 | offset = anOffset; 63 | scale = aScale; 64 | } 65 | return self; 66 | } 67 | 68 | - (id) transformedValue:(id)value { 69 | CGFloat source = CHLayoutExtractCGFloat(value); 70 | CGFloat transformed = (source * scale) + offset; 71 | return [NSNumber numberWithCGFloat_ch:transformed]; 72 | } 73 | 74 | @end 75 | 76 | #if NS_BLOCKS_AVAILABLE 77 | @interface CHLayoutBlockValueTransformer : NSValueTransformer 78 | { 79 | CHLayoutTransformer transformer; 80 | } 81 | 82 | + (id) transformerWithBlock:(CHLayoutTransformer)block; 83 | - (id) initWithBlock:(CHLayoutTransformer)block; 84 | 85 | @end 86 | 87 | @implementation CHLayoutBlockValueTransformer 88 | 89 | + (id) transformerWithBlock:(CHLayoutTransformer)block { 90 | return [[[self alloc] initWithBlock:block] autorelease]; 91 | } 92 | 93 | - (id) initWithBlock:(CHLayoutTransformer)block { 94 | self = [super init]; 95 | if (self) { 96 | transformer = Block_copy(block); 97 | } 98 | return self; 99 | } 100 | 101 | - (void) dealloc { 102 | Block_release(transformer); 103 | [super dealloc]; 104 | } 105 | 106 | - (id) transformedValue:(id)value { 107 | CGFloat source = CHLayoutExtractCGFloat(value); 108 | CGFloat transformed = transformer(source); 109 | return [NSNumber numberWithCGFloat_ch:transformed]; 110 | } 111 | 112 | @end 113 | #endif 114 | 115 | 116 | #pragma mark - 117 | #pragma mark CHLayoutConstraint 118 | 119 | @implementation CHLayoutConstraint 120 | @synthesize attribute, sourceAttribute, sourceName; 121 | 122 | #pragma mark Basic Initializers 123 | 124 | + (id)constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr { 125 | return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr scale:1.0 offset:0.0]; 126 | } 127 | 128 | + (id)constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr offset:(CGFloat)offset { 129 | return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr scale:1.0 offset:offset]; 130 | } 131 | 132 | + (id)constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr scale:(CGFloat)scale offset:(CGFloat)offset { 133 | CHLayoutValueTransformer * t = [CHLayoutValueTransformer transformerWithOffset:offset scale:scale]; 134 | return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr valueTransformer:t]; 135 | } 136 | 137 | #if NS_BLOCKS_AVAILABLE 138 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr blockTransformer:(CHLayoutTransformer)transformer { 139 | CHLayoutBlockValueTransformer * t = [CHLayoutBlockValueTransformer transformerWithBlock:transformer]; 140 | return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr valueTransformer:t]; 141 | } 142 | #endif 143 | 144 | + (id) constraintWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr valueTransformer:(NSValueTransformer *)transformer { 145 | return [[[self alloc] initWithAttribute:attr relativeTo:srcLayer attribute:srcAttr valueTransformer:transformer] autorelease]; 146 | } 147 | 148 | - (id)initWithAttribute:(CHLayoutConstraintAttribute)attr relativeTo:(NSString *)srcLayer attribute:(CHLayoutConstraintAttribute)srcAttr valueTransformer:(NSValueTransformer *)transformer { 149 | double attributeRange = floor(log10(attr)); 150 | double sourceAttributeRange = floor(log10(srcAttr)); 151 | 152 | if (attributeRange != sourceAttributeRange) { 153 | [super dealloc]; 154 | [NSException raise:NSInvalidArgumentException format:@"Invalid source and target attributes"]; 155 | return nil; 156 | } 157 | 158 | self = [super init]; 159 | if (self) { 160 | attribute = attr; 161 | sourceAttribute = srcAttr; 162 | 163 | sourceName = [srcLayer copy]; 164 | valueTransformer = [transformer retain]; 165 | } 166 | return self; 167 | } 168 | 169 | - (void) dealloc { 170 | [valueTransformer release]; 171 | [sourceName release]; 172 | [super dealloc]; 173 | } 174 | 175 | - (CGFloat) transformValue:(CGFloat)original { 176 | id transformed = [valueTransformer transformedValue:[NSNumber numberWithCGFloat_ch:original]]; 177 | return CHLayoutExtractCGFloat(transformed); 178 | } 179 | 180 | - (void) applyToTargetView:(NSView *)target { 181 | NSView * source = [target relativeViewForName:[self sourceName]]; 182 | [self applyToTargetView:target sourceView:source]; 183 | } 184 | 185 | - (void) applyToTargetView:(NSView *)target sourceView:(NSView *)source { 186 | if (source == target) { return; } 187 | if (source == nil) { return; } 188 | if ([self sourceAttribute] == 0) { return; } 189 | 190 | NSRect sourceValue = [source valueForLayoutAttribute:[self sourceAttribute]]; 191 | 192 | NSRect targetValue = sourceValue; 193 | if (attribute >= CHLayoutConstraintAttributeMinY && attribute <= CHLayoutConstraintAttributeMidX) { 194 | targetValue.origin.x = [self transformValue:sourceValue.origin.x]; 195 | } 196 | 197 | [target setValue:targetValue forLayoutAttribute:[self attribute]]; 198 | } 199 | 200 | @end 201 | -------------------------------------------------------------------------------- /CHLayoutManager-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /CHLayoutManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayoutManager.h 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import 27 | 28 | @class CHLayoutConstraint; 29 | 30 | @interface CHLayoutManager : NSObject { 31 | BOOL hasRegistered; 32 | BOOL isProcessingChanges; 33 | 34 | NSMapTable * constraints; 35 | NSMutableArray * viewsToProcess; 36 | NSMutableSet * processedViews; 37 | } 38 | 39 | + (id) sharedLayoutManager; 40 | 41 | - (void) removeAllLayoutConstraints; 42 | 43 | - (void) addLayoutConstraint:(CHLayoutConstraint *)constraint toView:(NSView *)view; 44 | - (void) removeLayoutConstraintsFromView:(NSView *)view; 45 | - (NSArray *) layoutConstraintsOnView:(NSView *)view; 46 | - (NSString *) layoutNameForView:(NSView *)view; 47 | - (void) setLayoutName:(NSString *)name forView:(NSView *)view; 48 | 49 | - (void) beginProcessingView:(NSView *)aView; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /CHLayoutManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayoutManager.m 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import "CHLayoutManager.h" 27 | #import "CHLayoutConstraint.h" 28 | #import "NSView+CHLayout.h" 29 | #import 30 | 31 | @interface CHLayoutContainer : NSObject 32 | { 33 | NSString * layoutName; 34 | NSMutableArray * layoutConstraints; 35 | } 36 | 37 | @property (nonatomic, copy) NSString * layoutName; 38 | @property (readonly) NSMutableArray * layoutConstraints; 39 | 40 | @end 41 | 42 | @implementation CHLayoutContainer 43 | @synthesize layoutName, layoutConstraints; 44 | 45 | + (id) container { 46 | return [[[self alloc] init] autorelease]; 47 | } 48 | 49 | - (id) init { 50 | self = [super init]; 51 | if (self) { 52 | layoutConstraints = [[NSMutableArray alloc] init]; 53 | } 54 | return self; 55 | } 56 | 57 | - (void) dealloc { 58 | [layoutConstraints release]; 59 | [layoutName release]; 60 | [super dealloc]; 61 | } 62 | 63 | @end 64 | 65 | 66 | 67 | static CHLayoutManager * _sharedLayoutManager = nil; 68 | 69 | __attribute__((constructor)) 70 | static void construct_layoutManagerSingleton() { 71 | NSAutoreleasePool * p = [[NSAutoreleasePool alloc] init]; 72 | _sharedLayoutManager = [[CHLayoutManager alloc] init]; 73 | [p drain]; 74 | } 75 | 76 | __attribute__((destructor)) 77 | static void destroy_layoutManagerSingleton() { 78 | //since this happens at some point during teardown, I'm not sure there'll be an autorelease pool in place 79 | //but just in case.... make one anyway 80 | 81 | NSAutoreleasePool * p = [[NSAutoreleasePool alloc] init]; 82 | [_sharedLayoutManager release], _sharedLayoutManager = nil; 83 | [p drain]; 84 | } 85 | 86 | @implementation CHLayoutManager 87 | 88 | + (void) initialize { 89 | if (self == [CHLayoutManager class]) { 90 | Class nsview = [NSView class]; 91 | 92 | SEL dynamicDealloc = @selector(chlayoutautoremove_dynamicDealloc); 93 | 94 | Method newDealloc = class_getInstanceMethod(self, dynamicDealloc); 95 | if (newDealloc != NULL) { 96 | class_addMethod(nsview, dynamicDealloc, method_getImplementation(newDealloc), method_getTypeEncoding(newDealloc)); 97 | newDealloc = class_getInstanceMethod(nsview, dynamicDealloc); 98 | 99 | if (newDealloc != NULL) { 100 | Method originalDealloc = class_getInstanceMethod(nsview, @selector(dealloc)); 101 | method_exchangeImplementations(originalDealloc, newDealloc); 102 | } 103 | } 104 | } 105 | } 106 | 107 | + (id) sharedLayoutManager { 108 | return _sharedLayoutManager; 109 | } 110 | 111 | + (id) allocWithZone:(NSZone *)zone { 112 | if (_sharedLayoutManager) { 113 | return [_sharedLayoutManager retain]; 114 | } else { 115 | return [super allocWithZone:zone]; 116 | } 117 | } 118 | 119 | - (id) init { 120 | if (!_sharedLayoutManager) { 121 | self = [super init]; 122 | if (self) { 123 | //initialization goes here 124 | isProcessingChanges = NO; 125 | viewsToProcess = [[NSMutableArray alloc] init]; 126 | processedViews = [[NSMutableSet alloc] init]; 127 | 128 | constraints = [[NSMapTable mapTableWithWeakToStrongObjects] retain]; 129 | 130 | hasRegistered = NO; 131 | } 132 | } else if (self != _sharedLayoutManager) { 133 | [super dealloc]; 134 | self = _sharedLayoutManager; 135 | } 136 | return self; 137 | } 138 | 139 | - (void) dealloc { 140 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 141 | [self removeAllLayoutConstraints]; 142 | 143 | [viewsToProcess release]; 144 | [processedViews release]; 145 | [constraints release]; 146 | [super dealloc]; 147 | } 148 | 149 | - (void) removeAllLayoutConstraints { 150 | [constraints removeAllObjects]; 151 | } 152 | 153 | - (void) processView:(NSView *)aView { 154 | if (hasRegistered == NO) { 155 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameChanged:) name:NSViewFrameDidChangeNotification object:nil]; 156 | hasRegistered = YES; 157 | } 158 | [processedViews addObject:aView]; 159 | 160 | NSArray * viewConstraints = [self layoutConstraintsOnView:aView]; 161 | for (CHLayoutConstraint * constraint in viewConstraints) { 162 | [constraint applyToTargetView:aView]; 163 | } 164 | 165 | /** 166 | ORDER OF OPERATIONS: 167 | 1. See if this view has any siblings with constraints to this view 168 | 2. See if this view has any children with constraints to superview 169 | **/ 170 | 171 | //siblings constrained to this view 172 | //(if this view doesn't have a name, then a sibling can't be constrained to it) 173 | if ([self layoutNameForView:aView] != nil) { 174 | NSArray * superSubviews = [[aView superview] subviews]; 175 | for (NSView * subview in superSubviews) { 176 | if (subview == aView) { continue; } 177 | 178 | NSArray * subviewConstraints = [self layoutConstraintsOnView:subview]; 179 | for (CHLayoutConstraint * subviewConstraint in subviewConstraints) { 180 | NSView * sourceView = [subview relativeViewForName:[subviewConstraint sourceName]]; 181 | if (sourceView == aView) { 182 | [subviewConstraint applyToTargetView:subview sourceView:sourceView]; 183 | } 184 | } 185 | } 186 | } 187 | 188 | //subviews constrained to this view 189 | NSArray * subviews = [aView subviews]; 190 | for (NSView * subview in subviews) { 191 | NSArray * subviewConstraints = [self layoutConstraintsOnView:subview]; 192 | for (CHLayoutConstraint * subviewConstraint in subviewConstraints) { 193 | NSView * sourceView = [subview relativeViewForName:[subviewConstraint sourceName]]; 194 | if (sourceView == aView) { 195 | [subviewConstraint applyToTargetView:subview sourceView:sourceView]; 196 | } 197 | } 198 | } 199 | } 200 | 201 | - (void) beginProcessingView:(NSView *)view { 202 | if (isProcessingChanges == NO) { 203 | isProcessingChanges = YES; 204 | 205 | NSAutoreleasePool * viewPool = [[NSAutoreleasePool alloc] init]; 206 | [viewsToProcess removeAllObjects]; 207 | [processedViews removeAllObjects]; 208 | [viewsToProcess addObject:view]; 209 | 210 | while([viewsToProcess count] > 0) { 211 | NSView * currentView = [[viewsToProcess objectAtIndex:0] retain]; 212 | [viewsToProcess removeObjectAtIndex:0]; 213 | if ([viewsToProcess containsObject:currentView] == NO) { 214 | [self processView:currentView]; 215 | } 216 | [currentView release]; 217 | } 218 | 219 | [viewPool drain]; 220 | isProcessingChanges = NO; 221 | } else { 222 | if ([processedViews containsObject:view] == NO) { 223 | [viewsToProcess addObject:view]; 224 | } 225 | } 226 | } 227 | 228 | - (void) frameChanged:(NSNotification *)notification { 229 | NSView * view = [notification object]; 230 | [self beginProcessingView:view]; 231 | } 232 | 233 | #pragma mark - 234 | 235 | /** 236 | 237 | WHAT THE HECK IS GOING ON HERE: 238 | 239 | OK, so it turns out that NSKVONotifying_ objects (ie, objects with KV observers) *really* do not like being subclassed. 240 | Doing so will play silly buggers with your code and perhaps end up crashing (which is what was observed). 241 | 242 | So instead of dynamically subclassing the view (which was beautiful code *sniff*), we swizzle out the dealloc method. 243 | 244 | Here's what's going on: 245 | 246 | We have two selectors and two IMPs. Originally, things are set up like this: 247 | 248 | @selector(dealloc) => originalDeallocIMP 249 | 250 | Then we add the new dealloc method to the class (if it does not exist), so we now have: 251 | 252 | @selector(dealloc) => originalDeallocIMP 253 | @selector(chlayoutautoremove_dynamicDealloc) => dynamicDeallocIMP 254 | 255 | However, we want our custom dealloc method to get invoked first, so we swap their implementations, giving us: 256 | 257 | @selector(dealloc) => dynamicDeallocIMP 258 | @selector(chlayoutautoremove_dynamicDealloc) => originalDeallocIMP 259 | 260 | Now when the view gets deallocated, it's going to invoke the dynamic dealloc code (which cleans up layout information), 261 | and then invokes the original dealloc method, which does whatever it does, and also calls [super dealloc], 262 | and all is (hopefully) well in the world. 263 | 264 | **/ 265 | 266 | - (void) chlayoutautoremove_dynamicDealloc { 267 | if ([self isKindOfClass:[NSView class]]) { //to prevent people from being stupid 268 | [[CHLayoutManager sharedLayoutManager] removeLayoutConstraintsFromView:(NSView *)self]; 269 | [[CHLayoutManager sharedLayoutManager] setLayoutName:nil forView:(NSView *)self]; 270 | //THIS IS NOT A RECURSIVE CALL 271 | //see the big comment above for why 272 | [self chlayoutautoremove_dynamicDealloc]; 273 | } 274 | } 275 | 276 | - (void) addLayoutConstraint:(CHLayoutConstraint *)constraint toView:(NSView *)view { 277 | CHLayoutContainer * viewContainer = [constraints objectForKey:view]; 278 | if (viewContainer == nil) { 279 | viewContainer = [CHLayoutContainer container]; 280 | [constraints setObject:viewContainer forKey:view]; 281 | } 282 | 283 | [[viewContainer layoutConstraints] addObject:constraint]; 284 | [self beginProcessingView:view]; 285 | } 286 | 287 | - (void) removeLayoutConstraintsFromView:(NSView *)view { 288 | CHLayoutContainer * viewContainer = [constraints objectForKey:view]; 289 | [[viewContainer layoutConstraints] removeAllObjects]; 290 | 291 | if ([[viewContainer layoutConstraints] count] == 0 && [viewContainer layoutName] == nil) { 292 | [constraints removeObjectForKey:view]; 293 | } 294 | } 295 | 296 | - (NSArray *) layoutConstraintsOnView:(NSView *)view { 297 | CHLayoutContainer * container = [constraints objectForKey:view]; 298 | if (container == nil) { return [NSArray array]; } 299 | return [[[container layoutConstraints] copy] autorelease]; 300 | } 301 | 302 | - (NSString *) layoutNameForView:(NSView *)view { 303 | CHLayoutContainer * container = [constraints objectForKey:view]; 304 | return [container layoutName]; 305 | } 306 | 307 | - (void) setLayoutName:(NSString *)name forView:(NSView *)view { 308 | CHLayoutContainer * viewContainer = [constraints objectForKey:view]; 309 | 310 | if (name == nil && [[viewContainer layoutConstraints] count] == 0) { 311 | [constraints removeObjectForKey:view]; 312 | } else { 313 | if (viewContainer == nil) { 314 | viewContainer = [CHLayoutContainer container]; 315 | [constraints setObject:viewContainer forKey:view]; 316 | } 317 | [viewContainer setLayoutName:name]; 318 | } 319 | } 320 | 321 | @end 322 | -------------------------------------------------------------------------------- /CHLayoutManager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 11 | 256AC3DA0F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.m */; }; 12 | 551F7B8F121867DF00A573EE /* SinTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 551F7B8E121867DF00A573EE /* SinTransformer.m */; }; 13 | 553C2DB71335464700D93628 /* NSNumber+CHLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 553C2DB61335464700D93628 /* NSNumber+CHLayout.m */; }; 14 | 558686A31215C7910014B300 /* CHLayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 558686A21215C7910014B300 /* CHLayoutManager.m */; }; 15 | 558686A61215CBD40014B300 /* CHLayoutConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 558686A51215CBD40014B300 /* CHLayoutConstraint.m */; }; 16 | 558686C21215D0C40014B300 /* NSView+CHLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 558686C11215D0C40014B300 /* NSView+CHLayout.m */; }; 17 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 18 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 19 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 24 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 25 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 26 | 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 27 | 256AC3D80F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLayoutManagerAppDelegate.h; sourceTree = ""; }; 28 | 256AC3D90F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHLayoutManagerAppDelegate.m; sourceTree = ""; }; 29 | 256AC3F00F4B6AF500CF3369 /* CHLayoutManager_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLayoutManager_Prefix.pch; sourceTree = ""; }; 30 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 31 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 32 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 33 | 550C4453121663CC00E0002B /* CHLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLayout.h; sourceTree = ""; }; 34 | 551F7B8D121867DF00A573EE /* SinTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SinTransformer.h; sourceTree = ""; }; 35 | 551F7B8E121867DF00A573EE /* SinTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SinTransformer.m; sourceTree = ""; }; 36 | 553C2DB51335464700D93628 /* NSNumber+CHLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNumber+CHLayout.h"; sourceTree = ""; }; 37 | 553C2DB61335464700D93628 /* NSNumber+CHLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNumber+CHLayout.m"; sourceTree = ""; }; 38 | 558686A11215C7910014B300 /* CHLayoutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLayoutManager.h; sourceTree = ""; }; 39 | 558686A21215C7910014B300 /* CHLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHLayoutManager.m; sourceTree = ""; }; 40 | 558686A41215CBD40014B300 /* CHLayoutConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLayoutConstraint.h; sourceTree = ""; }; 41 | 558686A51215CBD40014B300 /* CHLayoutConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHLayoutConstraint.m; sourceTree = ""; }; 42 | 558686C01215D0C40014B300 /* NSView+CHLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+CHLayout.h"; sourceTree = ""; }; 43 | 558686C11215D0C40014B300 /* NSView+CHLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+CHLayout.m"; sourceTree = ""; }; 44 | 8D1107310486CEB800E47090 /* CHLayoutManager-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "CHLayoutManager-Info.plist"; sourceTree = ""; }; 45 | 8D1107320486CEB800E47090 /* CHLayoutManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHLayoutManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 080E96DDFE201D6D7F000001 /* Classes */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 550C4453121663CC00E0002B /* CHLayout.h */, 64 | 558686C01215D0C40014B300 /* NSView+CHLayout.h */, 65 | 558686C11215D0C40014B300 /* NSView+CHLayout.m */, 66 | 558686A11215C7910014B300 /* CHLayoutManager.h */, 67 | 558686A21215C7910014B300 /* CHLayoutManager.m */, 68 | 558686A41215CBD40014B300 /* CHLayoutConstraint.h */, 69 | 558686A51215CBD40014B300 /* CHLayoutConstraint.m */, 70 | 256AC3D80F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.h */, 71 | 256AC3D90F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.m */, 72 | 551F7B8D121867DF00A573EE /* SinTransformer.h */, 73 | 551F7B8E121867DF00A573EE /* SinTransformer.m */, 74 | 553C2DB51335464700D93628 /* NSNumber+CHLayout.h */, 75 | 553C2DB61335464700D93628 /* NSNumber+CHLayout.m */, 76 | ); 77 | name = Classes; 78 | sourceTree = ""; 79 | }; 80 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 84 | ); 85 | name = "Linked Frameworks"; 86 | sourceTree = ""; 87 | }; 88 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 92 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 93 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 94 | ); 95 | name = "Other Frameworks"; 96 | sourceTree = ""; 97 | }; 98 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 8D1107320486CEB800E47090 /* CHLayoutManager.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 29B97314FDCFA39411CA2CEA /* CHLayoutManager */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 080E96DDFE201D6D7F000001 /* Classes */, 110 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 111 | 29B97317FDCFA39411CA2CEA /* Resources */, 112 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 113 | 19C28FACFE9D520D11CA2CBB /* Products */, 114 | ); 115 | name = CHLayoutManager; 116 | sourceTree = ""; 117 | }; 118 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 256AC3F00F4B6AF500CF3369 /* CHLayoutManager_Prefix.pch */, 122 | 29B97316FDCFA39411CA2CEA /* main.m */, 123 | ); 124 | name = "Other Sources"; 125 | sourceTree = ""; 126 | }; 127 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 8D1107310486CEB800E47090 /* CHLayoutManager-Info.plist */, 131 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 132 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */, 133 | ); 134 | name = Resources; 135 | sourceTree = ""; 136 | }; 137 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 141 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 142 | ); 143 | name = Frameworks; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 8D1107260486CEB800E47090 /* CHLayoutManager */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "CHLayoutManager" */; 152 | buildPhases = ( 153 | 8D1107290486CEB800E47090 /* Resources */, 154 | 8D11072C0486CEB800E47090 /* Sources */, 155 | 8D11072E0486CEB800E47090 /* Frameworks */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = CHLayoutManager; 162 | productInstallPath = "$(HOME)/Applications"; 163 | productName = CHLayoutManager; 164 | productReference = 8D1107320486CEB800E47090 /* CHLayoutManager.app */; 165 | productType = "com.apple.product-type.application"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 171 | isa = PBXProject; 172 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CHLayoutManager" */; 173 | compatibilityVersion = "Xcode 3.1"; 174 | developmentRegion = English; 175 | hasScannedForEncodings = 1; 176 | knownRegions = ( 177 | English, 178 | Japanese, 179 | French, 180 | German, 181 | ); 182 | mainGroup = 29B97314FDCFA39411CA2CEA /* CHLayoutManager */; 183 | projectDirPath = ""; 184 | projectRoot = ""; 185 | targets = ( 186 | 8D1107260486CEB800E47090 /* CHLayoutManager */, 187 | ); 188 | }; 189 | /* End PBXProject section */ 190 | 191 | /* Begin PBXResourcesBuildPhase section */ 192 | 8D1107290486CEB800E47090 /* Resources */ = { 193 | isa = PBXResourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 197 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | /* End PBXResourcesBuildPhase section */ 202 | 203 | /* Begin PBXSourcesBuildPhase section */ 204 | 8D11072C0486CEB800E47090 /* Sources */ = { 205 | isa = PBXSourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | 8D11072D0486CEB800E47090 /* main.m in Sources */, 209 | 256AC3DA0F4B6AC300CF3369 /* CHLayoutManagerAppDelegate.m in Sources */, 210 | 558686A31215C7910014B300 /* CHLayoutManager.m in Sources */, 211 | 558686A61215CBD40014B300 /* CHLayoutConstraint.m in Sources */, 212 | 558686C21215D0C40014B300 /* NSView+CHLayout.m in Sources */, 213 | 551F7B8F121867DF00A573EE /* SinTransformer.m in Sources */, 214 | 553C2DB71335464700D93628 /* NSNumber+CHLayout.m in Sources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXSourcesBuildPhase section */ 219 | 220 | /* Begin PBXVariantGroup section */ 221 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { 222 | isa = PBXVariantGroup; 223 | children = ( 224 | 089C165DFE840E0CC02AAC07 /* English */, 225 | ); 226 | name = InfoPlist.strings; 227 | sourceTree = ""; 228 | }; 229 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = { 230 | isa = PBXVariantGroup; 231 | children = ( 232 | 1DDD58150DA1D0A300B32029 /* English */, 233 | ); 234 | name = MainMenu.xib; 235 | sourceTree = ""; 236 | }; 237 | /* End PBXVariantGroup section */ 238 | 239 | /* Begin XCBuildConfiguration section */ 240 | C01FCF4B08A954540054247B /* Debug */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | COPY_PHASE_STRIP = NO; 245 | GCC_DYNAMIC_NO_PIC = NO; 246 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 247 | GCC_MODEL_TUNING = G5; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 250 | GCC_PREFIX_HEADER = CHLayoutManager_Prefix.pch; 251 | INFOPLIST_FILE = "CHLayoutManager-Info.plist"; 252 | INSTALL_PATH = "$(HOME)/Applications"; 253 | PRODUCT_NAME = CHLayoutManager; 254 | }; 255 | name = Debug; 256 | }; 257 | C01FCF4C08A954540054247B /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 262 | GCC_MODEL_TUNING = G5; 263 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 264 | GCC_PREFIX_HEADER = CHLayoutManager_Prefix.pch; 265 | INFOPLIST_FILE = "CHLayoutManager-Info.plist"; 266 | INSTALL_PATH = "$(HOME)/Applications"; 267 | PRODUCT_NAME = CHLayoutManager; 268 | }; 269 | name = Release; 270 | }; 271 | C01FCF4F08A954540054247B /* Debug */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_ENABLE_OBJC_GC = supported; 277 | GCC_OPTIMIZATION_LEVEL = 0; 278 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | ONLY_ACTIVE_ARCH = YES; 281 | PREBINDING = NO; 282 | SDKROOT = macosx10.6; 283 | }; 284 | name = Debug; 285 | }; 286 | C01FCF5008A954540054247B /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 290 | GCC_C_LANGUAGE_STANDARD = gnu99; 291 | GCC_ENABLE_OBJC_GC = supported; 292 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 293 | GCC_WARN_UNUSED_VARIABLE = YES; 294 | PREBINDING = NO; 295 | SDKROOT = macosx10.6; 296 | }; 297 | name = Release; 298 | }; 299 | /* End XCBuildConfiguration section */ 300 | 301 | /* Begin XCConfigurationList section */ 302 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "CHLayoutManager" */ = { 303 | isa = XCConfigurationList; 304 | buildConfigurations = ( 305 | C01FCF4B08A954540054247B /* Debug */, 306 | C01FCF4C08A954540054247B /* Release */, 307 | ); 308 | defaultConfigurationIsVisible = 0; 309 | defaultConfigurationName = Release; 310 | }; 311 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CHLayoutManager" */ = { 312 | isa = XCConfigurationList; 313 | buildConfigurations = ( 314 | C01FCF4F08A954540054247B /* Debug */, 315 | C01FCF5008A954540054247B /* Release */, 316 | ); 317 | defaultConfigurationIsVisible = 0; 318 | defaultConfigurationName = Release; 319 | }; 320 | /* End XCConfigurationList section */ 321 | }; 322 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 323 | } 324 | -------------------------------------------------------------------------------- /CHLayoutManagerAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayoutManagerAppDelegate.h 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import 27 | 28 | @interface CHLayoutManagerAppDelegate : NSObject { 29 | NSWindow *window; 30 | 31 | IBOutlet NSSlider * widthSlider; 32 | IBOutlet NSButton * button1; 33 | IBOutlet NSButton * button2; 34 | IBOutlet NSProgressIndicator * progress; 35 | 36 | IBOutlet NSButton * leftVerticalButton; 37 | IBOutlet NSButton * rightVerticalButton; 38 | 39 | IBOutlet NSButton * helpButton; 40 | } 41 | 42 | @property (assign) IBOutlet NSWindow *window; 43 | 44 | - (IBAction) sliderChanged:(id)sender; 45 | 46 | - (IBAction) clickButton1:(id)sender; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /CHLayoutManagerAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CHLayoutManagerAppDelegate.m 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import "CHLayoutManagerAppDelegate.h" 27 | #import "CHLayout.h" 28 | #import "SinTransformer.h" 29 | 30 | 31 | @implementation CHLayoutManagerAppDelegate 32 | 33 | @synthesize window; 34 | 35 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 36 | // Insert code here to initialize your application 37 | 38 | [button1 setLayoutName:@"button1"]; 39 | [button2 addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMinX relativeTo:@"button1" attribute:CHLayoutConstraintAttributeMaxX]]; 40 | [button2 addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMaxY relativeTo:@"button1" attribute:CHLayoutConstraintAttributeMaxY]]; 41 | [button2 addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeWidth relativeTo:@"button1" attribute:CHLayoutConstraintAttributeWidth]]; 42 | 43 | [progress startAnimation:nil]; 44 | CHLayoutConstraint * centerHorizontal = [CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMidX relativeTo:@"superview" attribute:CHLayoutConstraintAttributeMidX]; 45 | CHLayoutConstraint * centerVertical = [CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMidY relativeTo:@"superview" attribute:CHLayoutConstraintAttributeMidY]; 46 | [progress addLayoutConstraint:centerHorizontal]; 47 | [progress addLayoutConstraint:centerVertical]; 48 | 49 | [progress addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMidXMidY relativeTo:@"superview" attribute:CHLayoutConstraintAttributeBoundsCenter]]; 50 | 51 | 52 | [leftVerticalButton setLayoutName:@"leftVertical"]; 53 | [leftVerticalButton addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMinX relativeTo:@"superview" attribute:CHLayoutConstraintAttributeMinX offset:37]]; 54 | [rightVerticalButton addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMaxX relativeTo:@"superview" attribute:CHLayoutConstraintAttributeMaxX offset:-13]]; 55 | [rightVerticalButton addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMinY relativeTo:@"leftVertical" attribute:CHLayoutConstraintAttributeMinY]]; 56 | 57 | #if NS_BLOCKS_AVAILABLE 58 | 59 | CHLayoutTransformer transformer = ^(CGFloat source) { 60 | CGFloat superViewHeight = [[rightVerticalButton superview] frame].size.height; 61 | return (superViewHeight - source); 62 | }; 63 | [rightVerticalButton addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMaxY relativeTo:@"leftVertical" attribute:CHLayoutConstraintAttributeMinY blockTransformer:transformer]]; 64 | 65 | #endif 66 | 67 | // [helpButton addConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMinX relativeTo:@"leftVertical" attribute:CHLayoutConstraintAttributeMinY scale:2.0 offset:0.0]]; 68 | [helpButton addLayoutConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMidXMidY relativeTo:@"button1" attribute:CHLayoutConstraintAttributeBoundsCenter]]; 69 | 70 | SinTransformer * sinTransformer = [[SinTransformer alloc] init]; 71 | // [helpButton addConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMinY relativeTo:@"button1" attribute:CHLayoutConstraintAttributeMinY valueTransformer:sinTransformer]]; 72 | [sinTransformer release]; 73 | 74 | // [helpButton addConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeCenter relativeTo:@"superview" attribute:CHLayoutConstraintAttributeFrame]]; 75 | 76 | [self sliderChanged:nil]; 77 | // [[CHLayoutManager sharedLayoutManager] beginProcessingView:[window contentView]]; 78 | } 79 | 80 | - (IBAction) sliderChanged:(id)sender { 81 | CGFloat sliderValue = [widthSlider floatValue]; 82 | NSRect button1Frame = [button1 frame]; 83 | button1Frame.size.width = sliderValue; 84 | button1Frame.origin.y = sliderValue; 85 | [button1 setFrame:button1Frame]; 86 | 87 | CGFloat range = ([widthSlider maxValue] - [widthSlider minValue]); 88 | CGFloat percentage = ((sliderValue - range)/range) * 0.9; 89 | NSRect leftVerticalFrame = [leftVerticalButton frame]; 90 | leftVerticalFrame.origin.y = ([[window contentView] frame].size.height * percentage); 91 | [leftVerticalButton setFrame:leftVerticalFrame]; 92 | } 93 | 94 | - (IBAction) clickButton1:(id)sender { 95 | 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /CHLayoutManager_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CHLayoutManager' target in the 'CHLayoutManager' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Dave DeLong 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | ***************************************************************************** 22 | 23 | Copyright (c) 2010 Dave DeLong 24 | 25 | Permission is hereby granted, free of charge, to EMC Corporation to deal in the 26 | software and associated documentation files (the "Software") without restriction, 27 | including without limitation the rights to use, copy, modify, merge, and/or 28 | distribute in binary form copies of the Software. This license overrides any 29 | other license present in the Software. 30 | 31 | This license is only applicable to copies of the software acquired from 32 | http://github.com/davedelong/CHLayoutManager -------------------------------------------------------------------------------- /NSNumber+CHLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+CHLayout.h 3 | // CHLayoutManager 4 | // 5 | // Created by Dave DeLong on 3/19/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSNumber (CHLayout) 13 | 14 | + (id)numberWithCGFloat_ch:(CGFloat)cgFloat; 15 | - (id)initWithCGFloat_ch:(CGFloat)cgFloat; 16 | 17 | - (CGFloat)CGFloatValue_ch; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /NSNumber+CHLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+CHLayout.m 3 | // CHLayoutManager 4 | // 5 | // Created by Dave DeLong on 3/19/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "NSNumber+CHLayout.h" 10 | 11 | 12 | @implementation NSNumber (CHLayout) 13 | 14 | + (id)numberWithCGFloat_ch:(CGFloat)cgFloat { 15 | return [[[self alloc] initWithCGFloat_ch:cgFloat] autorelease]; 16 | } 17 | 18 | - (id)initWithCGFloat_ch:(CGFloat)cgFloat { 19 | #if CGFLOAT_IS_DOUBLE 20 | return [self initWithDouble:cgFloat]; 21 | #else 22 | return [self initWithFloat:cgFloat]; 23 | #endif 24 | } 25 | 26 | - (CGFloat)CGFloatValue_ch { 27 | #if CGFLOAT_IS_DOUBLE 28 | return [self doubleValue]; 29 | #else 30 | return [self floatValue]; 31 | #endif 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /NSView+CHLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+CHLayout.h 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import 27 | #import "CHLayoutConstraint.h" 28 | 29 | @interface NSView (CHLayout) 30 | 31 | - (void) setLayoutName:(NSString *)newLayoutName; 32 | - (NSString *) layoutName; 33 | 34 | - (void) addLayoutConstraint:(CHLayoutConstraint *)constraint; 35 | - (NSArray *) layoutConstraints; 36 | - (void) removeAllLayoutConstraints; 37 | 38 | - (NSRect) valueForLayoutAttribute:(CHLayoutConstraintAttribute)attribute; 39 | - (void) setValue:(NSRect)newValue forLayoutAttribute:(CHLayoutConstraintAttribute)attribute; 40 | 41 | - (NSView *) relativeViewForName:(NSString *)name; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /NSView+CHLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+CHLayout.m 3 | // CHLayoutManager 4 | /** 5 | Copyright (c) 2010 Dave DeLong 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | **/ 25 | 26 | #import "NSView+CHLayout.h" 27 | #import "CHLayoutManager.h" 28 | 29 | #define CHScalarRect(_s) (NSMakeRect((_s), 0, 0, 0)) 30 | #define CHPointRect(_x,_y) (NSMakeRect((_x), (_y), 0, 0)) 31 | 32 | #define CHRectScalar(_r) ((_r).origin.x) 33 | #define CHRectPoint(_r) ((_r).origin) 34 | 35 | #define CHSetMinX(_r,_v) ((_r).origin.x = (_v)) 36 | #define CHSetMinY(_r,_v) ((_r).origin.y = (_v)) 37 | #define CHSetMidX(_r,_v) ((_r).origin.x = (_v) - ((_r).size.width/2)) 38 | #define CHSetMidY(_r,_v) ((_r).origin.y = (_v) - ((_r).size.height/2)) 39 | #define CHSetMaxX(_r,_v) ((_r).origin.x = (_v) - (_r).size.width) 40 | #define CHSetMaxY(_r,_v) ((_r).origin.y = (_v) - (_r).size.height) 41 | 42 | @implementation NSView (CHLayout) 43 | 44 | - (void) setLayoutName:(NSString *)newLayoutName { 45 | [[CHLayoutManager sharedLayoutManager] setLayoutName:newLayoutName forView:self]; 46 | } 47 | 48 | - (NSString *) layoutName { 49 | return [[CHLayoutManager sharedLayoutManager] layoutNameForView:self]; 50 | } 51 | 52 | - (void) addLayoutConstraint:(CHLayoutConstraint *)constraint { 53 | [[CHLayoutManager sharedLayoutManager] addLayoutConstraint:constraint toView:self]; 54 | } 55 | 56 | - (NSArray *) layoutConstraints { 57 | return [[CHLayoutManager sharedLayoutManager] layoutConstraintsOnView:self]; 58 | } 59 | 60 | - (void) removeAllLayoutConstraints { 61 | [[CHLayoutManager sharedLayoutManager] removeLayoutConstraintsFromView:self]; 62 | } 63 | 64 | - (NSRect) valueForLayoutAttribute:(CHLayoutConstraintAttribute)attribute { 65 | NSRect frame = [self frame]; 66 | NSRect bounds = [self bounds]; 67 | switch (attribute) { 68 | case CHLayoutConstraintAttributeMinY: 69 | return CHScalarRect(NSMinY(frame)); 70 | case CHLayoutConstraintAttributeMaxY: 71 | return CHScalarRect(NSMaxY(frame)); 72 | case CHLayoutConstraintAttributeMinX: 73 | return CHScalarRect(NSMinX(frame)); 74 | case CHLayoutConstraintAttributeMaxX: 75 | return CHScalarRect(NSMaxX(frame)); 76 | case CHLayoutConstraintAttributeWidth: 77 | return CHScalarRect(NSWidth(frame)); 78 | case CHLayoutConstraintAttributeHeight: 79 | return CHScalarRect(NSHeight(frame)); 80 | case CHLayoutConstraintAttributeMidY: 81 | return CHScalarRect(NSMidY(frame)); 82 | case CHLayoutConstraintAttributeMidX: 83 | return CHScalarRect(NSMidX(frame)); 84 | case CHLayoutConstraintAttributeMinXMinY: 85 | return CHPointRect(NSMinX(frame), NSMinY(frame)); 86 | case CHLayoutConstraintAttributeMinXMidY: 87 | return CHPointRect(NSMinX(frame), NSMidY(frame)); 88 | case CHLayoutConstraintAttributeMinXMaxY: 89 | return CHPointRect(NSMinX(frame), NSMaxY(frame)); 90 | case CHLayoutConstraintAttributeMidXMinY: 91 | return CHPointRect(NSMidX(frame), NSMinY(frame)); 92 | case CHLayoutConstraintAttributeMidXMidY: 93 | return CHPointRect(NSMidX(frame), NSMidY(frame)); 94 | case CHLayoutConstraintAttributeMidXMaxY: 95 | return CHPointRect(NSMidX(frame), NSMaxY(frame)); 96 | case CHLayoutConstraintAttributeMaxXMinY: 97 | return CHPointRect(NSMaxX(frame), NSMinY(frame)); 98 | case CHLayoutConstraintAttributeMaxXMidY: 99 | return CHPointRect(NSMaxX(frame), NSMidY(frame)); 100 | case CHLayoutConstraintAttributeMaxXMaxY: 101 | return CHPointRect(NSMaxX(frame), NSMaxY(frame)); 102 | case CHLayoutConstraintAttributeBoundsCenter: 103 | return CHPointRect(NSMidX(bounds), NSMidY(bounds)); 104 | case CHLayoutConstraintAttributeFrame: 105 | return frame; 106 | case CHLayoutConstraintAttributeBounds: 107 | return bounds; 108 | default: 109 | return NSZeroRect; 110 | } 111 | } 112 | 113 | - (void) setValue:(NSRect)newValue forLayoutAttribute:(CHLayoutConstraintAttribute)attribute { 114 | NSRect frame = [self frame]; 115 | NSRect bounds = [self bounds]; 116 | 117 | CGFloat scalarValue = CHRectScalar(newValue); 118 | NSPoint pointValue = CHRectPoint(newValue); 119 | NSRect rectValue = newValue; 120 | 121 | switch (attribute) { 122 | case CHLayoutConstraintAttributeMinY: 123 | CHSetMinY(frame, scalarValue); 124 | break; 125 | case CHLayoutConstraintAttributeMaxY: 126 | CHSetMaxY(frame, scalarValue); 127 | break; 128 | case CHLayoutConstraintAttributeMinX: 129 | CHSetMinX(frame, scalarValue); 130 | break; 131 | case CHLayoutConstraintAttributeMaxX: 132 | CHSetMaxX(frame, scalarValue); 133 | break; 134 | case CHLayoutConstraintAttributeWidth: 135 | frame.size.width = scalarValue; 136 | break; 137 | case CHLayoutConstraintAttributeHeight: 138 | frame.size.height = scalarValue; 139 | break; 140 | case CHLayoutConstraintAttributeMidY: 141 | CHSetMidY(frame, scalarValue); 142 | break; 143 | case CHLayoutConstraintAttributeMidX: 144 | CHSetMidX(frame, scalarValue); 145 | break; 146 | case CHLayoutConstraintAttributeMinXMinY: 147 | CHSetMinX(frame, pointValue.x); 148 | CHSetMinY(frame, pointValue.y); 149 | break; 150 | case CHLayoutConstraintAttributeMinXMidY: 151 | CHSetMinX(frame, pointValue.x); 152 | CHSetMidY(frame, pointValue.y); 153 | break; 154 | case CHLayoutConstraintAttributeMinXMaxY: 155 | CHSetMinX(frame, pointValue.x); 156 | CHSetMaxY(frame, pointValue.y); 157 | break; 158 | case CHLayoutConstraintAttributeMidXMinY: 159 | CHSetMidX(frame, pointValue.x); 160 | CHSetMinY(frame, pointValue.y); 161 | break; 162 | case CHLayoutConstraintAttributeMidXMidY: 163 | CHSetMidX(frame, pointValue.x); 164 | CHSetMidY(frame, pointValue.y); 165 | break; 166 | case CHLayoutConstraintAttributeBoundsCenter: 167 | CHSetMidX(bounds, pointValue.x); 168 | CHSetMidY(bounds, pointValue.y); 169 | break; 170 | case CHLayoutConstraintAttributeMidXMaxY: 171 | CHSetMidX(frame, pointValue.x); 172 | CHSetMaxY(frame, pointValue.y); 173 | break; 174 | case CHLayoutConstraintAttributeMaxXMinY: 175 | CHSetMaxX(frame, pointValue.x); 176 | CHSetMinY(frame, pointValue.y); 177 | break; 178 | case CHLayoutConstraintAttributeMaxXMidY: 179 | CHSetMaxX(frame, pointValue.x); 180 | CHSetMidY(frame, pointValue.y); 181 | break; 182 | case CHLayoutConstraintAttributeMaxXMaxY: 183 | CHSetMaxX(frame, pointValue.x); 184 | CHSetMaxY(frame, pointValue.y); 185 | break; 186 | case CHLayoutConstraintAttributeFrame: 187 | frame = rectValue; 188 | break; 189 | case CHLayoutConstraintAttributeBounds: 190 | bounds = rectValue; 191 | break; 192 | } 193 | 194 | if (attribute != CHLayoutConstraintAttributeBounds && attribute != CHLayoutConstraintAttributeBoundsCenter) { 195 | [self setFrame:frame]; 196 | } else { 197 | [self setBounds:bounds]; 198 | } 199 | } 200 | 201 | - (NSView *) relativeViewForName:(NSString *)name { 202 | if ([name isEqual:@"superview"]) { 203 | return [self superview]; 204 | } 205 | 206 | NSArray * superSubviews = [[self superview] subviews]; 207 | for (NSView *view in superSubviews) { 208 | if ([[view layoutName] isEqual:name]) { 209 | return (view == self ? nil : view); 210 | } 211 | } 212 | return nil; 213 | } 214 | 215 | @end 216 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | **This project is no longer considered under active development, and has been deprecated in favor of built-in API. Please see the "Supported Platforms" section for more information.** 2 | 3 | #CHLayoutManager 4 | 5 | CHLayoutManager is a way to add positioning and sizing constraints on views. The easiest way to understand this is with an example: 6 | 7 | ##Example 8 | 9 | [okButton setLayoutName:@"ok"]; 10 | CHLayoutConstraint * stayToTheLeft = [CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMaxX relativeTo:@"ok" attribute:CHLayoutConstraintAttributeMinX offset:-10]; 11 | [cancelButton addConstraint:stayToTheLeft]; 12 | 13 | Now, whenever `okButton` is resized or moved, `cancelButton` will be automatically moved to have its right edge positioned 10 pixels to the left of `okButton`'s left edge. 14 | 15 | ##Usage 16 | 17 | In order to use CHLayoutManager in your app, copy these 7 files into your project: 18 | 19 | - `CHLayout.h` 20 | - `CHLayoutManager.h` 21 | - `CHLayoutManager.m` 22 | - `CHLayoutConstraint.h` 23 | - `CHLayoutConstraint.m` 24 | - `NSView+CHLayout.h` 25 | - `NSView+CHLayout.m` 26 | 27 | Then `#import "CHLayout.h"` in any .m file that needs to apply constraints to views. 28 | 29 | ##Improvements over `CAConstraint` 30 | 31 | - `CHLayoutConstraints` support all the features of `CAConstraints` 32 | - `CHLayoutConstraints` allow you to specify an `NSValueTransformer` in order to do more complex transformations. 33 | - `CHLayoutConstraints` allow you to specify a block in order to do more complex transformations. 34 | - `CHLayoutConstraints` allow you to bind point and rect values, in addition to scalar values. 35 | 36 | (The included sample application shows how to use a `CHLayoutTransformer` block in order to apply constraints that are not possible via the normal mechanism.) 37 | 38 | ##Supported Platforms 39 | 40 | - Mac OS X 10.5 - 10.6. 41 | 42 | `CHLayoutManager` will not be supported beyond 10.6. `CHLayoutManager` will work on 10.7, but it is recommended that you use the layout system in 10.7 as opposed to this. While they are compatible, the mechanism in 10.7 is far more flexible. 43 | 44 | ##Special Considerations 45 | 46 | - It's possible to set up circular dependencies on constraints. Do so at your own risk. 47 | - In order to constrain a view to its superview, create a constraint with the `sourceName` of `@"superview"`. 48 | - It's very easy to create a [retain cycle][retain-cycle] if you use a block transformer that references `self` in a constraint, and then have `self` retain the constraint. ([Blocks retain objects that they capture][block-retain]) 49 | - You may add as many constraints to a view as you like. If a view has multiple constraints on the same attributed, they will all be evaluated in the order they were added. Beware. 50 | - If you are manually managing your memory (ie, not using garbage collection), then it is recommend that you use `[myView removeAllConstraints]` before the view is deallocated. Doing so will ensure that all constraints and value transformers associated with that view are properly cleaned up. If you are using garbage collection, these will be cleaned up for you. 51 | - Transformations (scale+offset, `NSValueTransformer`, and blocks-based) are only applied to scalar attributes. They are not applied to point- or rect-based attributes. 52 | - You cannot constrain an attribute to another attribute of a different type. In other words, you must constrain a scalar to a scalar, a point to a point, and a rect to a rect. 53 | 54 | ##License 55 | 56 | CHLayoutManager is licensed under the MIT license, which is reproduced in its entirety here: 57 | 58 | >Copyright (c) 2011 Dave DeLong 59 | > 60 | >Permission is hereby granted, free of charge, to any person obtaining a copy 61 | >of this software and associated documentation files (the "Software"), to deal 62 | >in the Software without restriction, including without limitation the rights 63 | >to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | >copies of the Software, and to permit persons to whom the Software is 65 | >furnished to do so, subject to the following conditions: 66 | > 67 | >The above copyright notice and this permission notice shall be included in 68 | >all copies or substantial portions of the Software. 69 | > 70 | >THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | >IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | >FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | >AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | >LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | >OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 76 | >THE SOFTWARE. 77 | 78 | 79 | [retain-cycle]: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043-1000810 80 | [block-retain]: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW4 -------------------------------------------------------------------------------- /SinTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SinTransformer.h 3 | // CHLayoutManager 4 | // 5 | // Created by Dave DeLong on 8/15/10. 6 | // Copyright 2010 Home. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface SinTransformer : NSValueTransformer { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SinTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // SinTransformer.m 3 | // CHLayoutManager 4 | // 5 | // Created by Dave DeLong on 8/15/10. 6 | // Copyright 2010 Home. All rights reserved. 7 | // 8 | 9 | #import "SinTransformer.h" 10 | 11 | 12 | @implementation SinTransformer 13 | 14 | - (id) transformedValue:(id)value { 15 | CGFloat source = [value floatValue]; 16 | CGFloat output = fabs((50*sinf(source))) + 20; 17 | return [NSNumber numberWithFloat:output]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CHLayoutManager 4 | // 5 | // Created by Dave DeLong on 8/13/10. 6 | // Copyright 2010 Home. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **) argv); 14 | } 15 | --------------------------------------------------------------------------------