├── .gitignore ├── FDStackView.podspec ├── FDStackView ├── FDGapLayoutGuide.h ├── FDGapLayoutGuide.m ├── FDLayoutSpacer.h ├── FDLayoutSpacer.m ├── FDStackView.h ├── FDStackView.m ├── FDStackViewAlignmentLayoutArrangement.h ├── FDStackViewAlignmentLayoutArrangement.m ├── FDStackViewDistributionLayoutArrangement.h ├── FDStackViewDistributionLayoutArrangement.m ├── FDStackViewExtensions.h ├── FDStackViewExtensions.m ├── FDStackViewLayoutArrangement.h ├── FDStackViewLayoutArrangement.m ├── FDTransformLayer.h └── FDTransformLayer.m ├── FDStackViewDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── FDStackViewDemo ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── FDCodeBasedViewController.h ├── FDCodeBasedViewController.m ├── FDStoryboardBasedViewController.h ├── FDStoryboardBasedViewController.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── forkingdog.imageset │ │ ├── 11923583.png │ │ └── Contents.json ├── Info.plist └── main.m ├── LICENSE ├── README.md └── Snapshots ├── snapshot0.png └── snapshot1.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | -------------------------------------------------------------------------------- /FDStackView.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "FDStackView" 4 | s.version = "1.0.1" 5 | s.summary = "Use UIStackView as if it supports iOS6+." 6 | 7 | s.description = <<-DESC 8 | # Problem 9 | 10 | UIStackView is a very handy tool to build flow layout, but it's available only when iOS9+, we've found some great compatible replacements like OAStackView, but we want more: 11 | 12 | - **Perfect downward compatible**, no infectivity, use UIStackView **directly** as if it's shipped from iOS6. 13 | - **Interface builder support**, live preview. 14 | - Keep layout constraints as closely as UIStackView constructs. 15 | 16 | # Usage 17 | 18 | **Import nothing, learn nothing, it just works.** 19 | 20 | - It will automatically replace the symbol for UIStackView into FDStackView at runtime before iOS9. 21 | 22 | ``` objc 23 | // Works in iOS6+, use it directly. 24 | UIStackView *stackView = [[UIStackView alloc] init]; 25 | stackView.axis = UILayoutConstraintAxisHorizontal; 26 | stackView.distribution = UIStackViewDistributionFill; 27 | stackView.alignment = UIStackViewAlignmentTop; 28 | [stackView addArrangedSubview:[[UILabel alloc] init]]; 29 | [self.view addSubview:stackView]; 30 | ``` 31 | 32 | - Interface Builder Support 33 | 34 | Set `Builds for` option to `iOS 9.0 and later` to eliminate the version error in Xcode: 35 | 36 | ![How to use in IB](https://raw.githubusercontent.com/forkingdog/FDStackView/master/Snapshots/snapshot0.png) 37 | 38 | Now, use UIStackView as you like and its reactive options and live preview: 39 | 40 | ![UIStackView preview in IB](https://raw.githubusercontent.com/forkingdog/FDStackView/master/Snapshots/snapshot1.png) 41 | 42 | # Requirements 43 | 44 | - Xcode 7+ (For interface builder supports and the latest Objective-C Syntax) 45 | - Base SDK iOS 9.0+ (To link UIStackView symbol in UIKit) 46 | DESC 47 | 48 | s.homepage = "https://github.com/forkingdog/FDStackView" 49 | 50 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 51 | s.license = { :type => "MIT", :file => "LICENSE" } 52 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 53 | s.author = { "forkingdog group" => "https://github.com/forkingdog" } 54 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 55 | s.platform = :ios, "6.0" 56 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 57 | s.source = { :git => "https://github.com/forkingdog/FDStackView.git", :tag => "1.0.1" } 58 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 59 | s.source_files = "FDStackView/*.{h,m}" 60 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 61 | s.requires_arc = true 62 | end 63 | -------------------------------------------------------------------------------- /FDStackView/FDGapLayoutGuide.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface FDGapLayoutGuide : UIView 26 | @property (nonatomic, readonly) UIView *owningView; 27 | @end 28 | -------------------------------------------------------------------------------- /FDStackView/FDGapLayoutGuide.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDGapLayoutGuide.h" 24 | #import "FDTransformLayer.h" 25 | 26 | @implementation FDGapLayoutGuide 27 | 28 | + (Class)layerClass { 29 | return FDTransformLayer.class; 30 | } 31 | 32 | - (UIView *)owningView { 33 | return self.superview; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /FDStackView/FDLayoutSpacer.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface FDLayoutSpacer : UIView 26 | @property (nonatomic, readonly) UIView *owningView; 27 | @property (nonatomic, assign, getter=isHorizontal) BOOL horizontal; 28 | @property (nonatomic, strong, readonly) NSMutableArray *systemConstraints; 29 | @end 30 | -------------------------------------------------------------------------------- /FDStackView/FDLayoutSpacer.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDLayoutSpacer.h" 24 | #import "FDTransformLayer.h" 25 | 26 | @implementation FDLayoutSpacer 27 | @synthesize systemConstraints = _systemConstraints; 28 | 29 | + (Class)layerClass { 30 | return FDTransformLayer.class; 31 | } 32 | 33 | - (NSMutableArray *)systemConstraints { 34 | if (!_systemConstraints) { 35 | _systemConstraints = [NSMutableArray array]; 36 | } 37 | return _systemConstraints; 38 | } 39 | 40 | - (UIView *)owningView { 41 | return self.superview; 42 | } 43 | 44 | @end -------------------------------------------------------------------------------- /FDStackView/FDStackView.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | #if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000 26 | #error "FDStackView must be compiled under iOS9 SDK at least" 27 | #endif 28 | 29 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000 30 | #warning "No need for FDStackView with a deploy iOS version greater than 9.0" 31 | #endif 32 | 33 | // No need to use this class directly, it is the internal class that actually works before iOS9. 34 | @interface FDStackView : UIView 35 | 36 | - (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views; 37 | 38 | @property (nonatomic, copy, readonly) NSArray<__kindof UIView *> *arrangedSubviews; 39 | 40 | - (void)addArrangedSubview:(UIView *)view; 41 | - (void)removeArrangedSubview:(UIView *)view; 42 | - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex; 43 | 44 | @property (nonatomic, assign) UILayoutConstraintAxis axis; 45 | @property (nonatomic, assign) UIStackViewDistribution distribution; 46 | @property (nonatomic, assign) UIStackViewAlignment alignment; 47 | @property (nonatomic, assign) CGFloat spacing; 48 | @property (nonatomic, assign, getter=isBaselineRelativeArrangement) BOOL baselineRelativeArrangement; 49 | @property (nonatomic, assign, getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /FDStackView/FDStackView.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDStackView.h" 24 | #import 25 | #import "FDTransformLayer.h" 26 | #import "FDStackViewAlignmentLayoutArrangement.h" 27 | #import "FDStackViewDistributionLayoutArrangement.h" 28 | 29 | @interface FDStackView () 30 | @property (nonatomic, strong) NSMutableArray *mutableArrangedSubviews; 31 | @property (nonatomic, strong) FDStackViewAlignmentLayoutArrangement *alignmentArrangement; 32 | @property (nonatomic, strong) FDStackViewDistributionLayoutArrangement *distributionArrangement; 33 | @end 34 | 35 | @implementation FDStackView 36 | 37 | + (Class)layerClass { 38 | return FDTransformLayer.class; 39 | } 40 | 41 | - (id)initWithCoder:(NSCoder *)decoder { 42 | self = [super initWithCoder:decoder]; 43 | if (self) { 44 | // Attributes of UIStackView in interface builder that archived. 45 | [self commonInitializationWithArrangedSubviews:[decoder decodeObjectForKey:@"UIStackViewArrangedSubviews"]]; 46 | self.axis = [decoder decodeIntegerForKey:@"UIStackViewAxis"]; 47 | self.distribution = [decoder decodeIntegerForKey:@"UIStackViewDistribution"]; 48 | self.alignment = [decoder decodeIntegerForKey:@"UIStackViewAlignment"]; 49 | self.spacing = [decoder decodeDoubleForKey:@"UIStackViewSpacing"]; 50 | self.baselineRelativeArrangement = [decoder decodeBoolForKey:@"UIStackViewBaselineRelative"]; 51 | self.layoutMarginsRelativeArrangement = [decoder decodeBoolForKey:@"UIStackViewLayoutMarginsRelative"]; 52 | } 53 | return self; 54 | } 55 | 56 | - (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views { 57 | self = [super initWithFrame:CGRectZero]; 58 | if (self) { 59 | [self commonInitializationWithArrangedSubviews:views]; 60 | } 61 | return self; 62 | } 63 | 64 | - (instancetype)initWithFrame:(CGRect)frame { 65 | self = [super initWithFrame:frame]; 66 | if (self) { 67 | [self commonInitializationWithArrangedSubviews:nil]; 68 | } 69 | return self; 70 | } 71 | 72 | - (void)commonInitializationWithArrangedSubviews:(NSArray<__kindof UIView *> *)arrangedSubviews { 73 | self.mutableArrangedSubviews = (arrangedSubviews ?: @[]).mutableCopy; 74 | for (UIView *view in self.mutableArrangedSubviews) { 75 | [self addHiddenObserverForView:view]; 76 | } 77 | 78 | self.distributionArrangement = [[FDStackViewDistributionLayoutArrangement alloc] initWithItems:arrangedSubviews onAxis:self.axis]; 79 | self.distributionArrangement.canvas = self; 80 | self.alignmentArrangement = [[FDStackViewAlignmentLayoutArrangement alloc] initWithItems:arrangedSubviews onAxis:self.axis]; 81 | self.alignmentArrangement.canvas = self; 82 | for (UIView *view in arrangedSubviews) { 83 | view.translatesAutoresizingMaskIntoConstraints = NO; 84 | [self addSubview:view]; 85 | } 86 | } 87 | 88 | #pragma mark - Public Arranged Subviews Operations 89 | 90 | - (NSArray *)arrangedSubviews { 91 | return self.mutableArrangedSubviews.copy; 92 | } 93 | 94 | - (void)addArrangedSubview:(UIView *)view { 95 | if (!view || [self.mutableArrangedSubviews containsObject:view]) { 96 | return; 97 | } 98 | [self addSubview:view]; 99 | view.translatesAutoresizingMaskIntoConstraints = NO; 100 | [self.mutableArrangedSubviews addObject:view]; 101 | [self.alignmentArrangement addItem:view]; 102 | [self.distributionArrangement addItem:view]; 103 | [self addHiddenObserverForView:view]; 104 | [self updateLayoutArrangements]; 105 | } 106 | 107 | - (void)removeArrangedSubview:(UIView *)view { 108 | if (![self.mutableArrangedSubviews containsObject:view] || ![view isDescendantOfView:self]) { 109 | return; 110 | } 111 | [self removeHiddenObserverForView:view]; 112 | [self.mutableArrangedSubviews removeObject:view]; 113 | [view removeFromSuperview]; 114 | [self.alignmentArrangement removeItem:view]; 115 | [self.distributionArrangement removeItem:view]; 116 | [self updateLayoutArrangements]; 117 | } 118 | 119 | - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex { 120 | if (!view || [self.mutableArrangedSubviews containsObject:view]) { 121 | return; 122 | } 123 | view.translatesAutoresizingMaskIntoConstraints = NO; 124 | [self insertSubview:view atIndex:stackIndex]; 125 | [self.mutableArrangedSubviews insertObject:view atIndex:stackIndex]; 126 | [self.alignmentArrangement insertItem:view atIndex:stackIndex]; 127 | [self.distributionArrangement insertItem:view atIndex:stackIndex]; 128 | [self addHiddenObserverForView:view]; 129 | [self updateLayoutArrangements]; 130 | } 131 | 132 | #pragma mark - Public Setters 133 | 134 | - (void)setAxis:(UILayoutConstraintAxis)axis { 135 | if (_axis != axis) { 136 | _axis = axis; 137 | self.distributionArrangement.axis = axis; 138 | self.alignmentArrangement.axis = axis; 139 | [self updateLayoutArrangements]; 140 | } 141 | } 142 | 143 | - (void)setDistribution:(UIStackViewDistribution)distribution { 144 | if (_distribution != distribution) { 145 | _distribution = distribution; 146 | self.distributionArrangement.distribution = distribution; 147 | [self updateLayoutArrangements]; 148 | } 149 | } 150 | 151 | - (void)setAlignment:(UIStackViewAlignment)alignment { 152 | if (_alignment != alignment) { 153 | _alignment = alignment; 154 | self.alignmentArrangement.alignment = alignment; 155 | [self updateLayoutArrangements]; 156 | } 157 | } 158 | 159 | - (void)setSpacing:(CGFloat)spacing { 160 | if (_spacing != spacing) { 161 | _spacing = spacing; 162 | self.distributionArrangement.spacing = spacing; 163 | [self updateLayoutArrangements]; 164 | } 165 | } 166 | 167 | #pragma mark - Intrinsic Content Size Invalidation 168 | 169 | // Use non-public API in UIView directly is dangerous, so we inject at runtime. 170 | + (void)load { 171 | static dispatch_once_t onceToken; 172 | dispatch_once(&onceToken, ^{ 173 | SEL selector = NSSelectorFromString(@"_intrinsicContentSizeInvalidatedForChildView:"); 174 | Method method = class_getInstanceMethod(self, @selector(intrinsicContentSizeInvalidatedForChildView:)); 175 | class_addMethod(self, selector, method_getImplementation(method), method_getTypeEncoding(method)); 176 | }); 177 | } 178 | 179 | - (void)intrinsicContentSizeInvalidatedForChildView:(UIView *)childView { 180 | [self.distributionArrangement intrinsicContentSizeInvalidatedForItem:childView]; 181 | [self.alignmentArrangement intrinsicContentSizeInvalidatedForItem:childView]; 182 | } 183 | 184 | #pragma mark - Layout 185 | 186 | - (void)updateLayoutArrangements { 187 | [self.distributionArrangement removeDeprecatedConstraints]; 188 | [self.alignmentArrangement removeDeprecatedConstraints]; 189 | [self.distributionArrangement updateArrangementConstraints]; 190 | [self.alignmentArrangement updateArrangementConstraints]; 191 | } 192 | 193 | - (void)updateConstraints { 194 | [self updateLayoutArrangements]; 195 | [super updateConstraints]; 196 | } 197 | 198 | #pragma mark - Hidden KVO 199 | 200 | static void *FDStackViewHiddenObservingContext = &FDStackViewHiddenObservingContext; 201 | 202 | - (void)addHiddenObserverForView:(UIView *)view { 203 | [view addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:FDStackViewHiddenObservingContext]; 204 | } 205 | 206 | - (void)removeHiddenObserverForView:(UIView *)view { 207 | [view removeObserver:self forKeyPath:@"hidden" context:FDStackViewHiddenObservingContext]; 208 | } 209 | 210 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)view change:(NSDictionary *)change context:(void *)context { 211 | 212 | if (context != FDStackViewHiddenObservingContext) { 213 | return; 214 | } 215 | 216 | BOOL oldValue = [change[NSKeyValueChangeOldKey] boolValue]; 217 | BOOL newValue = [change[NSKeyValueChangeNewKey] boolValue]; 218 | if (newValue == oldValue) { 219 | return; 220 | } 221 | 222 | [self.alignmentArrangement updateArrangementConstraints]; 223 | [self.distributionArrangement updateArrangementConstraints]; 224 | } 225 | 226 | - (void)dealloc { 227 | for (UIView *view in _mutableArrangedSubviews) { 228 | [self removeHiddenObserverForView:view]; 229 | } 230 | } 231 | 232 | @end 233 | 234 | // ---------------------------------------------------- 235 | // Runtime injection start. 236 | // Assemble codes below are based on: 237 | // https://github.com/0xced/NSUUID/blob/master/NSUUID.m 238 | // ---------------------------------------------------- 239 | 240 | #pragma mark - Runtime Injection 241 | 242 | __asm( 243 | ".section __DATA,__objc_classrefs,regular,no_dead_strip\n" 244 | #if TARGET_RT_64_BIT 245 | ".align 3\n" 246 | "L_OBJC_CLASS_UIStackView:\n" 247 | ".quad _OBJC_CLASS_$_UIStackView\n" 248 | #else 249 | ".align 2\n" 250 | "_OBJC_CLASS_UIStackView:\n" 251 | ".long _OBJC_CLASS_$_UIStackView\n" 252 | #endif 253 | ".weak_reference _OBJC_CLASS_$_UIStackView\n" 254 | ); 255 | 256 | // Constructors are called after all classes have been loaded. 257 | __attribute__((constructor)) static void FDStackViewPatchEntry(void) { 258 | static dispatch_once_t onceToken; 259 | dispatch_once(&onceToken, ^{ 260 | @autoreleasepool { 261 | 262 | // >= iOS9. 263 | if (objc_getClass("UIStackView")) { 264 | return; 265 | } 266 | 267 | Class *stackViewClassLocation = NULL; 268 | 269 | #if TARGET_CPU_ARM 270 | __asm("movw %0, :lower16:(_OBJC_CLASS_UIStackView-(LPC0+4))\n" 271 | "movt %0, :upper16:(_OBJC_CLASS_UIStackView-(LPC0+4))\n" 272 | "LPC0: add %0, pc" : "=r"(stackViewClassLocation)); 273 | #elif TARGET_CPU_ARM64 274 | __asm("adrp %0, L_OBJC_CLASS_UIStackView@PAGE\n" 275 | "add %0, %0, L_OBJC_CLASS_UIStackView@PAGEOFF" : "=r"(stackViewClassLocation)); 276 | #elif TARGET_CPU_X86_64 277 | __asm("leaq L_OBJC_CLASS_UIStackView(%%rip), %0" : "=r"(stackViewClassLocation)); 278 | #elif TARGET_CPU_X86 279 | void *pc = NULL; 280 | __asm("calll L0\n" 281 | "L0: popl %0\n" 282 | "leal _OBJC_CLASS_UIStackView-L0(%0), %1" : "=r"(pc), "=r"(stackViewClassLocation)); 283 | #else 284 | #error Unsupported CPU 285 | #endif 286 | 287 | if (stackViewClassLocation && !*stackViewClassLocation) { 288 | Class class = objc_allocateClassPair(FDStackView.class, "UIStackView", 0); 289 | if (class) { 290 | objc_registerClassPair(class); 291 | *stackViewClassLocation = class; 292 | } 293 | } 294 | } 295 | }); 296 | } 297 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewAlignmentLayoutArrangement.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDStackViewLayoutArrangement.h" 24 | 25 | @interface FDStackViewAlignmentLayoutArrangement : FDStackViewLayoutArrangement 26 | @property (nonatomic, assign) UIStackViewAlignment alignment; 27 | @property (nonatomic, strong) NSMutableDictionary *alignmentConstraints; 28 | @property (nonatomic, strong) NSMapTable *hiddingDimensionConstraints; 29 | @end 30 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewAlignmentLayoutArrangement.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDStackViewAlignmentLayoutArrangement.h" 24 | #import "FDStackViewExtensions.h" 25 | #import "FDLayoutSpacer.h" 26 | 27 | @interface FDStackViewAlignmentLayoutArrangement () 28 | @property (nonatomic, assign) BOOL spanningGuideConstraintsNeedUpdate; 29 | @property (nonatomic, strong) FDLayoutSpacer *spanningLayoutGuide; 30 | @end 31 | 32 | @implementation FDStackViewAlignmentLayoutArrangement 33 | 34 | #pragma mark - Setter / Getter 35 | 36 | - (NSMutableDictionary *)alignmentConstraints { 37 | if (!_alignmentConstraints) { 38 | _alignmentConstraints = [NSMutableDictionary dictionary]; 39 | } 40 | return _alignmentConstraints; 41 | } 42 | 43 | - (NSMapTable *)hiddingDimensionConstraints { 44 | if (!_hiddingDimensionConstraints) { 45 | _hiddingDimensionConstraints = [NSMapTable weakToWeakObjectsMapTable]; 46 | } 47 | return _hiddingDimensionConstraints; 48 | } 49 | 50 | - (NSString *)alignmentConstraintsFirstKey { 51 | if (self.axis == UILayoutConstraintAxisHorizontal) { 52 | switch (self.alignment) { 53 | case UIStackViewAlignmentFill: 54 | return @"Bottom"; 55 | case UIStackViewAlignmentTop: 56 | case UIStackViewAlignmentCenter: 57 | case UIStackViewAlignmentBottom: 58 | case UIStackViewAlignmentFirstBaseline: 59 | case UIStackViewAlignmentLastBaseline: 60 | return @"Ambiguity Suppression"; 61 | default: 62 | return @"Not Supported"; 63 | } 64 | } else { 65 | switch (self.alignment) { 66 | case UIStackViewAlignmentFill: 67 | return @"Leading"; 68 | case UIStackViewAlignmentLeading: 69 | case UIStackViewAlignmentCenter: 70 | case UIStackViewAlignmentTrailing: 71 | return @"Ambiguity Suppression"; 72 | default: 73 | return @"Not Supported"; 74 | } 75 | } 76 | } 77 | 78 | - (NSString *)alignmentConstraintsSecondKey { 79 | if (self.axis == UILayoutConstraintAxisHorizontal) { 80 | switch (self.alignment) { 81 | case UIStackViewAlignmentBottom: 82 | case UIStackViewAlignmentLastBaseline: 83 | return @"Bottom"; 84 | case UIStackViewAlignmentCenter: 85 | return @"CenterY"; 86 | case UIStackViewAlignmentTop: 87 | case UIStackViewAlignmentFill: 88 | case UIStackViewAlignmentFirstBaseline: 89 | return @"Top"; 90 | default: 91 | return @"Not Supported"; 92 | } 93 | } else { 94 | switch (self.alignment) { 95 | case UIStackViewAlignmentLeading: 96 | return @"Leading"; 97 | case UIStackViewAlignmentCenter: 98 | return @"CenterX"; 99 | case UIStackViewAlignmentTrailing: 100 | case UIStackViewAlignmentFill: 101 | return @"Trailing"; 102 | default: 103 | return @"Not Supported"; 104 | } 105 | } 106 | } 107 | 108 | - (NSLayoutAttribute)alignmentConstraintsFirstAttribute { 109 | if (self.axis == UILayoutConstraintAxisHorizontal) { 110 | switch (self.alignment) { 111 | case UIStackViewAlignmentFill: 112 | return NSLayoutAttributeBottom; 113 | case UIStackViewAlignmentTop: 114 | case UIStackViewAlignmentCenter: 115 | case UIStackViewAlignmentBottom: 116 | case UIStackViewAlignmentFirstBaseline: 117 | case UIStackViewAlignmentLastBaseline: 118 | return self.dimensionAttributeForCurrentAxis; 119 | default: 120 | return NSLayoutAttributeNotAnAttribute; 121 | } 122 | } else { 123 | switch (self.alignment) { 124 | case UIStackViewAlignmentFill: 125 | return NSLayoutAttributeLeading; 126 | case UIStackViewAlignmentLeading: 127 | case UIStackViewAlignmentCenter: 128 | case UIStackViewAlignmentTrailing: 129 | return self.dimensionAttributeForCurrentAxis; 130 | default: 131 | return NSLayoutAttributeNotAnAttribute; 132 | } 133 | } 134 | } 135 | 136 | - (NSLayoutAttribute)alignmentConstraintsSecondAttribute { 137 | if (self.axis == UILayoutConstraintAxisHorizontal) { 138 | switch (self.alignment) { 139 | case UIStackViewAlignmentBottom: 140 | return NSLayoutAttributeBottom; 141 | case UIStackViewAlignmentCenter: 142 | return NSLayoutAttributeCenterY; 143 | case UIStackViewAlignmentTop: 144 | case UIStackViewAlignmentFill: 145 | return NSLayoutAttributeTop; 146 | case UIStackViewAlignmentFirstBaseline: 147 | return NSLayoutAttributeFirstBaseline; 148 | case UIStackViewAlignmentLastBaseline: 149 | return NSLayoutAttributeLastBaseline; 150 | default: 151 | return NSLayoutAttributeNotAnAttribute; 152 | } 153 | } else { 154 | switch (self.alignment) { 155 | case UIStackViewAlignmentLeading: 156 | return NSLayoutAttributeLeading; 157 | case UIStackViewAlignmentCenter: 158 | return NSLayoutAttributeCenterX; 159 | case UIStackViewAlignmentTrailing: 160 | case UIStackViewAlignmentFill: 161 | return NSLayoutAttributeTrailing; 162 | default: 163 | return NSLayoutAttributeNotAnAttribute; 164 | } 165 | } 166 | } 167 | 168 | - (FDLayoutSpacer *)spanningLayoutGuide { 169 | if (!_spanningLayoutGuide) { 170 | [self createSpanningLayoutGuide]; 171 | _spanningGuideConstraintsNeedUpdate = YES; 172 | [self updateSpanningLayoutGuideConstraintsIfNecessary]; 173 | } 174 | return _spanningLayoutGuide; 175 | } 176 | 177 | - (void)setAxis:(UILayoutConstraintAxis)axis { 178 | if (self.axis != axis) { 179 | [super setAxis:axis]; 180 | [self.spanningLayoutGuide removeFromSuperview]; 181 | self.spanningLayoutGuide = nil; 182 | [self configureValidAlignment:&_alignment forAxis:axis]; 183 | } 184 | } 185 | 186 | - (void)setAlignment:(UIStackViewAlignment)alignment { 187 | if (_alignment != alignment) { 188 | [self configureValidAlignment:&alignment forAxis:self.axis]; 189 | _alignment = alignment; 190 | _spanningGuideConstraintsNeedUpdate = YES; 191 | } 192 | } 193 | 194 | #pragma mark - Override Methods 195 | 196 | - (void)removeDeprecatedConstraints { 197 | [self.alignmentConstraints enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSMapTable *mapTable, BOOL *stop) { 198 | [self.canvas removeConstraints:mapTable.fd_allObjects]; 199 | }]; 200 | [self.alignmentConstraints removeAllObjects]; 201 | [self.canvas removeConstraints:self.hiddingDimensionConstraints.fd_allObjects]; 202 | [self.hiddingDimensionConstraints removeAllObjects]; 203 | } 204 | 205 | - (NSLayoutAttribute)minAttributeForCanvasConnections { 206 | return self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeTop : NSLayoutAttributeLeading; 207 | } 208 | 209 | - (NSLayoutAttribute)centerAttributeForCanvasConnections { 210 | return self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX; 211 | } 212 | 213 | - (NSLayoutAttribute)maxAttributeForCanvasConnections { 214 | return self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeBottom : NSLayoutAttributeTrailing; 215 | } 216 | 217 | - (NSLayoutAttribute)dimensionAttributeForCurrentAxis { 218 | return self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeHeight : NSLayoutAttributeWidth; 219 | } 220 | 221 | - (NSLayoutRelation)layoutRelationForCanvasConnectionForAttribute:(NSLayoutAttribute)attribute { 222 | switch (self.alignment) { 223 | case UIStackViewAlignmentFirstBaseline: { 224 | if (attribute == self.minAttributeForCanvasConnections) { 225 | return NSLayoutRelationGreaterThanOrEqual; 226 | } 227 | break; 228 | } 229 | case UIStackViewAlignmentLastBaseline: { 230 | if (attribute == self.maxAttributeForCanvasConnections) { 231 | return NSLayoutRelationLessThanOrEqual; 232 | } 233 | break; 234 | } 235 | default: 236 | break; 237 | } 238 | return NSLayoutRelationEqual; 239 | } 240 | 241 | - (NSLayoutRelation)layoutRelationForItemConnectionForAttribute:(NSLayoutAttribute)attribute { 242 | switch (self.alignment) { 243 | case UIStackViewAlignmentCenter: 244 | case UIStackViewAlignmentFirstBaseline: 245 | case UIStackViewAlignmentLastBaseline: { 246 | if (attribute == self.minAttributeForCanvasConnections) { 247 | return NSLayoutRelationLessThanOrEqual; 248 | } else if (attribute == self.maxAttributeForCanvasConnections) { 249 | return NSLayoutRelationGreaterThanOrEqual; 250 | } 251 | break; 252 | } 253 | case UIStackViewAlignmentTop: { 254 | if (attribute == self.maxAttributeForCanvasConnections) { 255 | return NSLayoutRelationGreaterThanOrEqual; 256 | } 257 | break; 258 | } 259 | case UIStackViewAlignmentBottom: { 260 | if (attribute == self.minAttributeForCanvasConnections) { 261 | return NSLayoutRelationLessThanOrEqual; 262 | } 263 | break; 264 | } 265 | default: 266 | break; 267 | } 268 | return NSLayoutRelationEqual; 269 | } 270 | 271 | - (void)updateArrangementConstraints { 272 | [self updateSpanningLayoutGuideConstraintsIfNecessary]; 273 | [super updateArrangementConstraints]; 274 | [self updateAlignmentItemsConstraintsIfNecessary]; 275 | } 276 | 277 | - (void)updateCanvasConnectionConstraintsIfNecessary { 278 | if (self.mutableItems.count == 0) { 279 | return; 280 | } 281 | 282 | [self.canvas removeConstraints:self.canvasConnectionConstraints]; 283 | [self.canvasConnectionConstraints removeAllObjects]; 284 | 285 | NSArray *canvasAttributes = @[@(self.minAttributeForCanvasConnections), @(self.maxAttributeForCanvasConnections)]; 286 | if (self.alignment == UIStackViewAlignmentCenter) { 287 | canvasAttributes = [canvasAttributes arrayByAddingObject:@(self.centerAttributeForCanvasConnections)]; 288 | } else if (self.isBaselineAlignment) { 289 | NSLayoutConstraint *canvasFitConstraint = [NSLayoutConstraint constraintWithItem:self.canvas attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]; 290 | canvasFitConstraint.identifier = @"FDSV-canvas-fit"; 291 | canvasFitConstraint.priority = 49; 292 | [self.canvas addConstraint:canvasFitConstraint]; 293 | [self.canvasConnectionConstraints addObject:canvasFitConstraint]; 294 | } 295 | 296 | [canvasAttributes enumerateObjectsUsingBlock:^(NSNumber *canvasAttribute, NSUInteger idx, BOOL *stop) { 297 | NSLayoutAttribute attribute = canvasAttribute.integerValue; 298 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:[self viewOrGuideForLocationAttribute:attribute] attribute:attribute relatedBy:[self layoutRelationForCanvasConnectionForAttribute:attribute] toItem:self.canvas attribute:attribute multiplier:1 constant:0]; 299 | constraint.identifier = @"FDSV-canvas-connection"; 300 | [self.canvas addConstraint:constraint]; 301 | [self.canvasConnectionConstraints addObject:constraint]; 302 | }]; 303 | } 304 | 305 | - (UIView *)viewOrGuideForLocationAttribute:(NSLayoutAttribute)attribute { 306 | switch (self.alignment) { 307 | case UIStackViewAlignmentFill: 308 | return self.mutableItems.firstObject; 309 | case UIStackViewAlignmentTop: 310 | case UIStackViewAlignmentFirstBaseline: { 311 | if (attribute == self.minAttributeForCanvasConnections) { 312 | return self.mutableItems.firstObject; 313 | } 314 | break; 315 | } 316 | case UIStackViewAlignmentCenter: { 317 | if (attribute == self.centerAttributeForCanvasConnections) { 318 | return self.mutableItems.firstObject; 319 | } 320 | break; 321 | } 322 | case UIStackViewAlignmentBottom: 323 | case UIStackViewAlignmentLastBaseline: { 324 | if (attribute == self.maxAttributeForCanvasConnections) { 325 | return self.mutableItems.firstObject; 326 | } 327 | break; 328 | } 329 | default: 330 | break; 331 | } 332 | return self.spanningLayoutGuide; 333 | } 334 | 335 | #pragma mark - Private Methods 336 | 337 | - (void)createSpanningLayoutGuide { 338 | [_spanningLayoutGuide removeFromSuperview]; 339 | _spanningLayoutGuide = [FDLayoutSpacer new]; 340 | _spanningLayoutGuide.translatesAutoresizingMaskIntoConstraints = NO; 341 | [self.canvas addSubview:_spanningLayoutGuide]; 342 | _spanningLayoutGuide.horizontal = self.axis == UILayoutConstraintAxisHorizontal; 343 | } 344 | 345 | - (BOOL)spanningGuideConstraintsNeedUpdate { 346 | if (self.alignment != UIStackViewAlignmentFill && _spanningGuideConstraintsNeedUpdate) { 347 | _spanningGuideConstraintsNeedUpdate = NO; 348 | return YES; 349 | } 350 | return NO; 351 | } 352 | 353 | - (BOOL)isBaselineAlignment { 354 | return self.axis == UILayoutConstraintAxisHorizontal && (self.alignment == UIStackViewAlignmentFirstBaseline || self.alignment == UIStackViewAlignmentLastBaseline); 355 | } 356 | 357 | - (void)configureValidAlignment:(UIStackViewAlignment *)alignment forAxis:(UILayoutConstraintAxis)axis { 358 | if (axis == UILayoutConstraintAxisVertical && (*alignment == UIStackViewAlignmentFirstBaseline || *alignment == UIStackViewAlignmentLastBaseline)) { 359 | NSLog(@"Invalid for vertical axis. Use Leading or Trailing instead."); 360 | *alignment = *alignment == UIStackViewAlignmentFirstBaseline ? UIStackViewAlignmentLeading : UIStackViewAlignmentTrailing; 361 | } 362 | } 363 | 364 | - (void)updateSpanningLayoutGuideConstraintsIfNecessary { 365 | if (self.mutableItems.count == 0) { 366 | return; 367 | } 368 | 369 | if (self.spanningLayoutGuide && self.spanningGuideConstraintsNeedUpdate) { 370 | [self.canvas removeConstraints:self.spanningLayoutGuide.systemConstraints]; 371 | [self.spanningLayoutGuide.systemConstraints removeAllObjects]; 372 | 373 | //FDSV-spanning-fit 374 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.spanningLayoutGuide attribute:self.spanningLayoutGuide.isHorizontal ? NSLayoutAttributeWidth : NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]; 375 | constraint.priority = 51; 376 | constraint.identifier = @"FDSV-spanning-fit"; 377 | [self.canvas addConstraint:constraint]; 378 | [self.spanningLayoutGuide.systemConstraints addObject:constraint]; 379 | 380 | //FDSV-spanning-boundary 381 | [self.mutableItems enumerateObjectsUsingBlock:^(UIView *item, NSUInteger idx, BOOL *stop) { 382 | NSLayoutConstraint *minConstraint = [NSLayoutConstraint constraintWithItem:self.spanningLayoutGuide attribute:self.minAttributeForCanvasConnections relatedBy:[self layoutRelationForItemConnectionForAttribute:self.minAttributeForCanvasConnections] toItem:item attribute:self.minAttributeForCanvasConnections multiplier:1 constant:0]; 383 | minConstraint.identifier = @"FDSV-spanning-boundary"; 384 | minConstraint.priority = 999.5; 385 | [self.canvas addConstraint:minConstraint]; 386 | [self.spanningLayoutGuide.systemConstraints addObject:minConstraint]; 387 | 388 | NSLayoutConstraint *maxConstraint = [NSLayoutConstraint constraintWithItem:self.spanningLayoutGuide attribute:self.maxAttributeForCanvasConnections relatedBy:[self layoutRelationForItemConnectionForAttribute:self.maxAttributeForCanvasConnections] toItem:item attribute:self.maxAttributeForCanvasConnections multiplier:1 constant:0]; 389 | maxConstraint.identifier = @"FDSV-spanning-boundary"; 390 | maxConstraint.priority = 999.5; 391 | [self.canvas addConstraint:maxConstraint]; 392 | [self.spanningLayoutGuide.systemConstraints addObject:maxConstraint]; 393 | }]; 394 | } 395 | } 396 | 397 | - (void)updateAlignmentItemsConstraintsIfNecessary { 398 | if (self.mutableItems.count == 0) { 399 | return; 400 | } 401 | 402 | [self.alignmentConstraints setObject:[NSMapTable weakToWeakObjectsMapTable] forKey:self.alignmentConstraintsFirstKey]; 403 | [self.alignmentConstraints setObject:[NSMapTable weakToWeakObjectsMapTable] forKey:self.alignmentConstraintsSecondKey]; 404 | [self.canvas removeConstraints:self.hiddingDimensionConstraints.fd_allObjects]; 405 | [self.hiddingDimensionConstraints removeAllObjects]; 406 | 407 | UIView *guardView = self.mutableItems.firstObject; 408 | [self.mutableItems enumerateObjectsUsingBlock:^(UIView *item, NSUInteger idx, BOOL *stop) { 409 | if (self.alignment != UIStackViewAlignmentFill) { 410 | NSLayoutConstraint *ambiguitySuppressionConstraint = [NSLayoutConstraint constraintWithItem:item attribute:self.alignmentConstraintsFirstAttribute relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]; 411 | ambiguitySuppressionConstraint.identifier = @"FDSV-ambiguity-suppression"; 412 | ambiguitySuppressionConstraint.priority = 25; 413 | [item addConstraint:ambiguitySuppressionConstraint]; 414 | [self.alignmentConstraints[self.alignmentConstraintsFirstKey] setObject:ambiguitySuppressionConstraint forKey:item]; 415 | } else { 416 | if (item != guardView) { 417 | NSLayoutConstraint *firstConstraint = [NSLayoutConstraint constraintWithItem:guardView attribute:self.alignmentConstraintsFirstAttribute relatedBy:NSLayoutRelationEqual toItem:item attribute:self.alignmentConstraintsFirstAttribute multiplier:1 constant:0]; 418 | firstConstraint.identifier = @"FDSV-alignment"; 419 | [self.canvas addConstraint:firstConstraint]; 420 | [self.alignmentConstraints[self.alignmentConstraintsFirstKey] setObject:firstConstraint forKey:item]; 421 | } 422 | } 423 | if (item != guardView) { 424 | NSLayoutConstraint *secondConstraint = [NSLayoutConstraint constraintWithItem:guardView attribute:self.alignmentConstraintsSecondAttribute relatedBy:NSLayoutRelationEqual toItem:item attribute:self.alignmentConstraintsSecondAttribute multiplier:1 constant:0]; 425 | secondConstraint.identifier = @"FDSV-alignment"; 426 | [self.canvas addConstraint:secondConstraint]; 427 | [self.alignmentConstraints[self.alignmentConstraintsSecondKey] setObject:secondConstraint forKey:item]; 428 | } 429 | if (item.hidden) { 430 | NSLayoutConstraint *hiddenConstraint = [NSLayoutConstraint constraintWithItem:item attribute:self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeHeight : NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]; 431 | hiddenConstraint.priority = [item contentCompressionResistancePriorityForAxis:self.axis == UILayoutConstraintAxisHorizontal ? UILayoutConstraintAxisVertical : UILayoutConstraintAxisHorizontal]; 432 | hiddenConstraint.identifier = @"FDSV-hiding"; 433 | [self.canvas addConstraint:hiddenConstraint]; 434 | [self.hiddingDimensionConstraints setObject:hiddenConstraint forKey:item]; 435 | } 436 | }]; 437 | } 438 | 439 | @end 440 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewDistributionLayoutArrangement.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | #import "FDStackViewLayoutArrangement.h" 25 | 26 | @interface FDStackViewDistributionLayoutArrangement : FDStackViewLayoutArrangement 27 | @property (nonatomic, assign) CGFloat spacing; 28 | @property (nonatomic, assign) UIStackViewDistribution distribution; 29 | - (void)resetAllConstraints; 30 | @end 31 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewDistributionLayoutArrangement.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDStackViewDistributionLayoutArrangement.h" 24 | #import "FDStackViewExtensions.h" 25 | #import "FDGapLayoutGuide.h" 26 | 27 | @interface FDStackViewDistributionLayoutArrangement () 28 | 29 | @property (nonatomic, strong) NSMapTable *spacingOrCenteringGuides; 30 | @property (nonatomic, strong) NSMapTable *edgeToEdgeConstraints; 31 | @property (nonatomic, strong) NSMapTable *relatedDimensionConstraints; 32 | @property (nonatomic, strong) NSMapTable *hiddingDimensionConstraints; 33 | 34 | @end 35 | 36 | @implementation FDStackViewDistributionLayoutArrangement 37 | 38 | - (instancetype)initWithItems:(NSArray *)items onAxis:(UILayoutConstraintAxis)axis { 39 | self = [super initWithItems:items onAxis:axis]; 40 | if (self) { 41 | _spacingOrCenteringGuides = [NSMapTable weakToWeakObjectsMapTable]; 42 | _edgeToEdgeConstraints = [NSMapTable weakToWeakObjectsMapTable]; 43 | _relatedDimensionConstraints = [NSMapTable weakToWeakObjectsMapTable]; 44 | _hiddingDimensionConstraints = [NSMapTable weakToWeakObjectsMapTable]; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)setCanvas:(FDStackView *)canvas { 50 | [super setCanvas:canvas]; 51 | } 52 | 53 | - (NSLayoutRelation)edgeToEdgeRelation { 54 | return self.distribution >= UIStackViewDistributionEqualSpacing ? NSLayoutRelationGreaterThanOrEqual : NSLayoutRelationEqual; 55 | } 56 | 57 | - (void)resetFillEffect { 58 | // spacing - edge to edge 59 | [self.canvas removeConstraints:self.edgeToEdgeConstraints.fd_allObjects]; 60 | [self.edgeToEdgeConstraints removeAllObjects]; 61 | [self.canvas removeConstraints:self.hiddingDimensionConstraints.fd_allObjects]; 62 | [self.hiddingDimensionConstraints removeAllObjects]; 63 | 64 | UIView *offset = self.items.car; 65 | UIView *last = self.items.lastObject; 66 | for (UIView *view in self.items.cdr) { 67 | NSLayoutAttribute attribute = [self minAttributeForGapConstraint]; 68 | NSLayoutRelation relation = [self edgeToEdgeRelation]; 69 | NSLayoutConstraint *spacing = [NSLayoutConstraint constraintWithItem:view attribute:attribute relatedBy:relation toItem:offset attribute:attribute + 1 multiplier:1 constant:self.spacing]; 70 | spacing.identifier = @"FDSV-spacing"; 71 | [self.canvas addConstraint:spacing]; 72 | [self.edgeToEdgeConstraints setObject:spacing forKey:offset]; 73 | if (offset.hidden || (view == last && view.hidden)) { 74 | spacing.constant = 0; 75 | } 76 | offset = view; 77 | } 78 | // hidding dimensions 79 | for (UIView *view in self.items) { 80 | if (view.hidden) { 81 | NSLayoutAttribute dimensionAttribute = [self dimensionAttributeForCurrentAxis]; 82 | NSLayoutConstraint *dimensionConstraint = [NSLayoutConstraint constraintWithItem:view attribute:dimensionAttribute relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]; 83 | dimensionConstraint.identifier = @"FDSV-hiding"; 84 | [self.canvas addConstraint:dimensionConstraint]; 85 | [self.hiddingDimensionConstraints setObject:dimensionConstraint forKey:view]; 86 | } 87 | } 88 | } 89 | 90 | - (void)resetEquallyEffect { 91 | [self.canvas removeConstraints:self.relatedDimensionConstraints.fd_allObjects]; 92 | [self.relatedDimensionConstraints removeAllObjects]; 93 | 94 | NSArray *visiableViews = self.visiableItems; 95 | UIView *offset = visiableViews.car; 96 | CGFloat order = 0; 97 | for (UIView *view in visiableViews.cdr) { 98 | NSLayoutAttribute attribute = [self dimensionAttributeForCurrentAxis]; 99 | NSLayoutRelation relation = NSLayoutRelationEqual; 100 | CGFloat multiplier = self.distribution == UIStackViewDistributionFillEqually ? 1 : ({ 101 | CGSize size1 = offset.intrinsicContentSize; 102 | CGSize size2 = view.intrinsicContentSize; 103 | CGFloat multiplier = 1; 104 | if (attribute == NSLayoutAttributeWidth) { 105 | multiplier = size1.width / size2.width; 106 | } else { 107 | multiplier = size1.height / size2.height; 108 | } 109 | if (CGSizeEqualToSize(size1, CGSizeZero) || 110 | CGSizeEqualToSize(size2, CGSizeZero)) { 111 | multiplier = 1; 112 | } 113 | 114 | multiplier; 115 | }); 116 | NSLayoutConstraint *equally = [NSLayoutConstraint constraintWithItem:offset attribute:attribute relatedBy:relation toItem:view attribute:attribute multiplier:multiplier constant:0]; 117 | equally.priority = UILayoutPriorityRequired - (++order); 118 | equally.identifier = self.distribution == UIStackViewDistributionFillEqually ? @"FDSV-fill-equally" : @"FDSV-fill-proportionally"; 119 | [self.canvas addConstraint:equally]; 120 | [self.relatedDimensionConstraints setObject:equally forKey:offset]; 121 | 122 | offset = view; 123 | } 124 | } 125 | 126 | - (NSArray *)visiableItems { 127 | return [self.items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"hidden=NO"]]; 128 | } 129 | 130 | - (void)resetGapEffect { 131 | [self resetSpacingOrCenteringGuides]; 132 | [self resetSpacingOrCenteringGuideRelatedDimensionConstraints]; 133 | } 134 | 135 | - (void)resetSpacingOrCenteringGuides { 136 | [self.spacingOrCenteringGuides.fd_allObjects makeObjectsPerformSelector:@selector(removeFromSuperview)]; 137 | [self.spacingOrCenteringGuides removeAllObjects]; 138 | NSArray *visiableItems = self.visiableItems; 139 | if (visiableItems.count <= 1) { 140 | return; 141 | } 142 | 143 | [[visiableItems subarrayWithRange:(NSRange){0, visiableItems.count - 1}] enumerateObjectsUsingBlock:^(UIView *item, NSUInteger idx, BOOL *stop) { 144 | FDGapLayoutGuide *guide = [FDGapLayoutGuide new]; 145 | [self.canvas addSubview:guide]; 146 | guide.translatesAutoresizingMaskIntoConstraints = NO; 147 | UIView *relatedToItem = visiableItems[idx+1]; 148 | 149 | NSLayoutAttribute minGapAttribute = [self minAttributeForGapConstraint]; 150 | NSLayoutAttribute minContentAttribute; 151 | NSLayoutAttribute maxContentAttribute; 152 | if (self.distribution == UIStackViewDistributionEqualCentering) { 153 | minContentAttribute = self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeCenterX : NSLayoutAttributeCenterY; 154 | maxContentAttribute = minContentAttribute; 155 | } else { 156 | minContentAttribute = minGapAttribute; 157 | maxContentAttribute = minGapAttribute + 1; 158 | } 159 | 160 | NSLayoutConstraint *beginGap = [NSLayoutConstraint constraintWithItem:guide attribute:minGapAttribute relatedBy:NSLayoutRelationEqual toItem:item attribute:maxContentAttribute multiplier:1 constant:0]; 161 | beginGap.identifier = @"FDSV-distributing-edge"; 162 | NSLayoutConstraint *endGap = [NSLayoutConstraint constraintWithItem:relatedToItem attribute:minContentAttribute relatedBy:NSLayoutRelationEqual toItem:guide attribute:minGapAttribute + 1 multiplier:1 constant:0]; 163 | endGap.identifier = @"FDSV-distributing-edge"; 164 | [self.canvas addConstraint:beginGap]; 165 | [self.canvas addConstraint:endGap]; 166 | 167 | [self.spacingOrCenteringGuides setObject:guide forKey:item]; 168 | }]; 169 | } 170 | 171 | - (void)resetSpacingOrCenteringGuideRelatedDimensionConstraints { 172 | [self.canvas removeConstraints:self.relatedDimensionConstraints.fd_allObjects]; 173 | NSArray *visiableItems = self.visiableItems; 174 | if (visiableItems.count <= 1) return; 175 | 176 | FDGapLayoutGuide *firstGapGuide = [self.spacingOrCenteringGuides objectForKey:visiableItems.car]; 177 | [self.spacingOrCenteringGuides.fd_allObjects enumerateObjectsUsingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) { 178 | if (firstGapGuide == obj) return; 179 | NSLayoutAttribute dimensionAttribute = [self dimensionAttributeForCurrentAxis]; 180 | NSLayoutConstraint *related = [NSLayoutConstraint constraintWithItem:firstGapGuide attribute:dimensionAttribute relatedBy:NSLayoutRelationEqual toItem:obj attribute:dimensionAttribute multiplier:1 constant:0]; 181 | related.identifier = @"FDSV-fill-equally"; 182 | [self.relatedDimensionConstraints setObject:related forKey:obj]; 183 | [self.canvas addConstraint:related]; 184 | }]; 185 | } 186 | 187 | - (void)resetCanvasConnectionsEffect { 188 | [self.canvas removeConstraints:self.canvasConnectionConstraints]; 189 | if (!self.items.count) return; 190 | 191 | NSMutableArray *canvasConnectionConstraints = [NSMutableArray new]; 192 | NSLayoutAttribute minAttribute = [self minAttributeForCanvasConnections]; 193 | NSLayoutConstraint *head = [NSLayoutConstraint constraintWithItem:self.canvas attribute:minAttribute relatedBy:NSLayoutRelationEqual toItem:self.items.firstObject attribute:minAttribute multiplier:1 constant:0]; 194 | [canvasConnectionConstraints addObject:head]; 195 | head.identifier = @"FDSV-canvas-connection"; 196 | 197 | NSLayoutConstraint *end = [NSLayoutConstraint constraintWithItem:self.canvas attribute:minAttribute + 1 relatedBy:NSLayoutRelationEqual toItem:self.items.lastObject attribute:minAttribute + 1 multiplier:1 constant:0]; 198 | [canvasConnectionConstraints addObject:end]; 199 | end.identifier = @"FDSV-canvas-connection"; 200 | 201 | self.canvasConnectionConstraints = canvasConnectionConstraints; 202 | [self.canvas addConstraints:canvasConnectionConstraints]; 203 | } 204 | 205 | - (void)resetAllConstraints { 206 | [self resetCanvasConnectionsEffect]; 207 | [self resetFillEffect]; 208 | 209 | switch (self.distribution) { 210 | case UIStackViewDistributionFillEqually: 211 | case UIStackViewDistributionFillProportionally: 212 | // related dimension 213 | [self resetEquallyEffect]; 214 | break; 215 | 216 | case UIStackViewDistributionEqualCentering: 217 | case UIStackViewDistributionEqualSpacing: 218 | // spacing or centering 219 | [self resetGapEffect]; 220 | break; 221 | 222 | default: 223 | break; 224 | } 225 | } 226 | 227 | - (void)removeDeprecatedConstraints { 228 | [self.canvas removeConstraints:self.edgeToEdgeConstraints.fd_allObjects]; 229 | [self.edgeToEdgeConstraints removeAllObjects]; 230 | 231 | [self.canvas removeConstraints:self.relatedDimensionConstraints.fd_allObjects]; 232 | [self.relatedDimensionConstraints removeAllObjects]; 233 | 234 | [self.canvas removeConstraints:self.hiddingDimensionConstraints.fd_allObjects]; 235 | [self.hiddingDimensionConstraints removeAllObjects]; 236 | 237 | [self.spacingOrCenteringGuides.fd_allObjects makeObjectsPerformSelector:@selector(removeFromSuperview)]; 238 | [self.spacingOrCenteringGuides removeAllObjects]; 239 | } 240 | 241 | - (NSLayoutRelation)layoutRelationForCanvasConnectionForAttribute:(NSLayoutAttribute)attribute { 242 | return NSLayoutRelationEqual; 243 | } 244 | 245 | - (NSLayoutAttribute)minAttributeForCanvasConnections { 246 | return self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeLeading : NSLayoutAttributeTop; 247 | } 248 | 249 | - (NSLayoutAttribute)centerAttributeForCanvasConnections { 250 | return NSLayoutAttributeCenterY - self.axis; // wtf 251 | } 252 | 253 | - (NSLayoutAttribute)dimensionAttributeForCurrentAxis { 254 | return NSLayoutAttributeWidth + self.axis; // wtf 255 | } 256 | 257 | - (NSLayoutAttribute)minAttributeForGapConstraint { 258 | return self.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeLeading : NSLayoutAttributeTop; 259 | } 260 | 261 | - (void)updateCanvasConnectionConstraintsIfNecessary { 262 | [self resetAllConstraints]; 263 | } 264 | 265 | @end 266 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewExtensions.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface NSMapTable (FDAllObjects) 26 | @property (nonatomic, readonly) NSArray *fd_allObjects; 27 | @end 28 | 29 | @interface NSArray<__covariant ObjectType> (FDCarAndCdr) 30 | @property (nonatomic, readonly) ObjectType car; 31 | @property (nonatomic, readonly) NSArray *cdr; 32 | @end 33 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewExtensions.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDStackViewExtensions.h" 24 | #import 25 | 26 | @implementation NSMapTable (FDAllObjects) 27 | 28 | - (NSArray *)fd_allObjects { 29 | return self.objectEnumerator.allObjects ?: @[]; 30 | } 31 | 32 | @end 33 | 34 | @implementation NSLayoutConstraint (FDStackViewExtensions) 35 | 36 | + (void)load { 37 | static dispatch_once_t onceToken; 38 | dispatch_once(&onceToken, ^{ 39 | // iOS6 patch 40 | if(!class_getProperty(self, "identifier")) { 41 | SEL getterSelector = sel_registerName("identifier"); 42 | class_addMethod(self, getterSelector, imp_implementationWithBlock(^(id self) { 43 | return objc_getAssociatedObject(self, getterSelector); 44 | }), "@@"); 45 | class_addMethod(self, sel_registerName("setIdentifier:"), imp_implementationWithBlock(^(id self, id value) { 46 | objc_setAssociatedObject(self, getterSelector, value, OBJC_ASSOCIATION_COPY_NONATOMIC); 47 | }), "v@:@"); 48 | } 49 | }); 50 | } 51 | 52 | @end 53 | 54 | @implementation NSArray (FDCarAndCdr) 55 | 56 | - (id)car { 57 | return self.firstObject; 58 | } 59 | 60 | - (NSArray *)cdr { 61 | if (self.count <= 1) return nil; 62 | return [self subarrayWithRange:(NSRange){1, self.count - 1}]; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /FDStackView/FDStackViewLayoutArrangement.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | #import "FDStackView.h" 25 | 26 | @protocol FDStackViewLayoutArrangementSubclassHook 27 | @optional 28 | @property (nonatomic, assign, readonly) NSLayoutAttribute dimensionAttributeForCurrentAxis; 29 | @property (nonatomic, assign, readonly) NSLayoutAttribute minAttributeForCanvasConnections; 30 | @property (nonatomic, assign, readonly) NSLayoutAttribute maxAttributeForCanvasConnections; 31 | @property (nonatomic, assign, readonly) NSLayoutAttribute centerAttributeForCanvasConnections; 32 | 33 | - (void)removeDeprecatedConstraints; 34 | - (void)updateCanvasConnectionConstraintsIfNecessary; 35 | - (NSLayoutRelation)layoutRelationForCanvasConnectionForAttribute:(NSLayoutAttribute)attribute; 36 | 37 | @end 38 | 39 | @interface FDStackViewLayoutArrangement : NSObject 40 | 41 | @property (nonatomic, weak) UIView *canvas; 42 | @property (nonatomic, assign) UILayoutConstraintAxis axis; 43 | @property (nonatomic, strong) NSMutableArray *mutableItems; 44 | @property (nonatomic, strong) NSMutableArray *canvasConnectionConstraints; 45 | 46 | - (instancetype)initWithItems:(NSArray *)items onAxis:(UILayoutConstraintAxis)axis; 47 | - (void)insertItem:(UIView *)item atIndex:(NSUInteger)index; 48 | - (void)removeItem:(UIView *)item; 49 | - (void)addItem:(UIView *)item; 50 | 51 | @property (nonatomic, copy, readonly) NSArray *items; 52 | 53 | - (void)intrinsicContentSizeInvalidatedForItem:(id)item; 54 | - (void)updateArrangementConstraints; 55 | 56 | @end -------------------------------------------------------------------------------- /FDStackView/FDStackViewLayoutArrangement.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDStackViewLayoutArrangement.h" 24 | #import "FDStackViewAlignmentLayoutArrangement.h" 25 | #import "FDStackViewDistributionLayoutArrangement.h" 26 | 27 | @implementation FDStackViewLayoutArrangement 28 | 29 | - (instancetype)initWithItems:(NSArray *)items onAxis:(UILayoutConstraintAxis)axis { 30 | if (self = [super init]) { 31 | _mutableItems = [[NSMutableArray alloc] initWithArray:items]; 32 | _canvasConnectionConstraints = [NSMutableArray array]; 33 | _axis = axis; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)addItem:(UIView *)item { 39 | [self.mutableItems addObject:item]; 40 | } 41 | 42 | - (void)removeItem:(UIView *)item { 43 | [self.mutableItems removeObject:item]; 44 | } 45 | 46 | - (void)insertItem:(UIView *)item atIndex:(NSUInteger)index { 47 | [self.mutableItems insertObject:item atIndex:index]; 48 | } 49 | 50 | - (NSArray *)items { 51 | return self.mutableItems.copy; 52 | } 53 | 54 | - (void)intrinsicContentSizeInvalidatedForItem:(id)item { 55 | [self updateArrangementConstraints]; 56 | } 57 | 58 | - (void)updateArrangementConstraints { 59 | [self updateCanvasConnectionConstraintsIfNecessary]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /FDStackView/FDTransformLayer.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface FDTransformLayer : CATransformLayer 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /FDStackView/FDTransformLayer.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "FDTransformLayer.h" 24 | 25 | @implementation FDTransformLayer 26 | 27 | - (void)setOpaque:(BOOL)opaque { 28 | // Clear CATransformLayer's warnings. 29 | } 30 | 31 | - (void)setBackgroundColor:(CGColorRef)backgroundColor { 32 | // Clear CATransformLayer's warnings. 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /FDStackViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 484BAB3E1BC67ACA00D86EBB /* FDGapLayoutGuide.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB2E1BC67ACA00D86EBB /* FDGapLayoutGuide.h */; }; 11 | 484BAB3F1BC67ACA00D86EBB /* FDGapLayoutGuide.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB2F1BC67ACA00D86EBB /* FDGapLayoutGuide.m */; }; 12 | 484BAB401BC67ACA00D86EBB /* FDGapLayoutGuide.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB2F1BC67ACA00D86EBB /* FDGapLayoutGuide.m */; }; 13 | 484BAB411BC67ACA00D86EBB /* FDLayoutSpacer.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB301BC67ACA00D86EBB /* FDLayoutSpacer.h */; }; 14 | 484BAB421BC67ACA00D86EBB /* FDLayoutSpacer.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB311BC67ACA00D86EBB /* FDLayoutSpacer.m */; }; 15 | 484BAB431BC67ACA00D86EBB /* FDLayoutSpacer.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB311BC67ACA00D86EBB /* FDLayoutSpacer.m */; }; 16 | 484BAB441BC67ACA00D86EBB /* FDStackView.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB321BC67ACA00D86EBB /* FDStackView.h */; }; 17 | 484BAB451BC67ACA00D86EBB /* FDStackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB331BC67ACA00D86EBB /* FDStackView.m */; }; 18 | 484BAB461BC67ACA00D86EBB /* FDStackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB331BC67ACA00D86EBB /* FDStackView.m */; }; 19 | 484BAB471BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB341BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.h */; }; 20 | 484BAB481BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB351BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m */; }; 21 | 484BAB491BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB351BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m */; }; 22 | 484BAB4A1BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB361BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.h */; }; 23 | 484BAB4B1BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB371BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m */; }; 24 | 484BAB4C1BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB371BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m */; }; 25 | 484BAB4D1BC67ACA00D86EBB /* FDStackViewExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB381BC67ACA00D86EBB /* FDStackViewExtensions.h */; }; 26 | 484BAB4E1BC67ACA00D86EBB /* FDStackViewExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB391BC67ACA00D86EBB /* FDStackViewExtensions.m */; }; 27 | 484BAB4F1BC67ACA00D86EBB /* FDStackViewExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB391BC67ACA00D86EBB /* FDStackViewExtensions.m */; }; 28 | 484BAB501BC67ACA00D86EBB /* FDStackViewLayoutArrangement.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB3A1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.h */; }; 29 | 484BAB511BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB3B1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m */; }; 30 | 484BAB521BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB3B1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m */; }; 31 | 484BAB531BC67ACA00D86EBB /* FDTransformLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 484BAB3C1BC67ACA00D86EBB /* FDTransformLayer.h */; }; 32 | 484BAB541BC67ACA00D86EBB /* FDTransformLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB3D1BC67ACA00D86EBB /* FDTransformLayer.m */; }; 33 | 484BAB551BC67ACA00D86EBB /* FDTransformLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB3D1BC67ACA00D86EBB /* FDTransformLayer.m */; }; 34 | 484BAB681BC8BDBC00D86EBB /* FDCodeBasedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 484BAB671BC8BDBC00D86EBB /* FDCodeBasedViewController.m */; }; 35 | 48A1298A1B9BE5E500EF9265 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 48A129891B9BE5E500EF9265 /* main.m */; }; 36 | 48A1298D1B9BE5E500EF9265 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 48A1298C1B9BE5E500EF9265 /* AppDelegate.m */; }; 37 | 48A129931B9BE5E500EF9265 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48A129911B9BE5E500EF9265 /* Main.storyboard */; }; 38 | 48A129951B9BE5E500EF9265 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 48A129941B9BE5E500EF9265 /* Images.xcassets */; }; 39 | 48A129981B9BE5E500EF9265 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 48A129961B9BE5E500EF9265 /* LaunchScreen.xib */; }; 40 | E153018E1BA8110C00C857EF /* FDStoryboardBasedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E153018D1BA8110C00C857EF /* FDStoryboardBasedViewController.m */; }; 41 | /* End PBXBuildFile section */ 42 | 43 | /* Begin PBXCopyFilesBuildPhase section */ 44 | 484BAAEF1BC66FC100D86EBB /* CopyFiles */ = { 45 | isa = PBXCopyFilesBuildPhase; 46 | buildActionMask = 2147483647; 47 | dstPath = "include/$(PRODUCT_NAME)"; 48 | dstSubfolderSpec = 16; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXCopyFilesBuildPhase section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 484BAAF11BC66FC100D86EBB /* libFDStackViewPatch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFDStackViewPatch.a; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 484BAB2E1BC67ACA00D86EBB /* FDGapLayoutGuide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDGapLayoutGuide.h; sourceTree = ""; }; 58 | 484BAB2F1BC67ACA00D86EBB /* FDGapLayoutGuide.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDGapLayoutGuide.m; sourceTree = ""; }; 59 | 484BAB301BC67ACA00D86EBB /* FDLayoutSpacer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDLayoutSpacer.h; sourceTree = ""; }; 60 | 484BAB311BC67ACA00D86EBB /* FDLayoutSpacer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDLayoutSpacer.m; sourceTree = ""; }; 61 | 484BAB321BC67ACA00D86EBB /* FDStackView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDStackView.h; sourceTree = ""; }; 62 | 484BAB331BC67ACA00D86EBB /* FDStackView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDStackView.m; sourceTree = ""; }; 63 | 484BAB341BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDStackViewAlignmentLayoutArrangement.h; sourceTree = ""; }; 64 | 484BAB351BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDStackViewAlignmentLayoutArrangement.m; sourceTree = ""; }; 65 | 484BAB361BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDStackViewDistributionLayoutArrangement.h; sourceTree = ""; }; 66 | 484BAB371BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDStackViewDistributionLayoutArrangement.m; sourceTree = ""; }; 67 | 484BAB381BC67ACA00D86EBB /* FDStackViewExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDStackViewExtensions.h; sourceTree = ""; }; 68 | 484BAB391BC67ACA00D86EBB /* FDStackViewExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDStackViewExtensions.m; sourceTree = ""; }; 69 | 484BAB3A1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDStackViewLayoutArrangement.h; sourceTree = ""; }; 70 | 484BAB3B1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDStackViewLayoutArrangement.m; sourceTree = ""; }; 71 | 484BAB3C1BC67ACA00D86EBB /* FDTransformLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDTransformLayer.h; sourceTree = ""; }; 72 | 484BAB3D1BC67ACA00D86EBB /* FDTransformLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDTransformLayer.m; sourceTree = ""; }; 73 | 484BAB661BC8BDBC00D86EBB /* FDCodeBasedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDCodeBasedViewController.h; sourceTree = ""; }; 74 | 484BAB671BC8BDBC00D86EBB /* FDCodeBasedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDCodeBasedViewController.m; sourceTree = ""; }; 75 | 48A129841B9BE5E500EF9265 /* FDStackViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FDStackViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 48A129881B9BE5E500EF9265 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | 48A129891B9BE5E500EF9265 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 78 | 48A1298B1B9BE5E500EF9265 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 79 | 48A1298C1B9BE5E500EF9265 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 80 | 48A129921B9BE5E500EF9265 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 81 | 48A129941B9BE5E500EF9265 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 82 | 48A129971B9BE5E500EF9265 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 83 | E153018C1BA8110C00C857EF /* FDStoryboardBasedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDStoryboardBasedViewController.h; sourceTree = ""; }; 84 | E153018D1BA8110C00C857EF /* FDStoryboardBasedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDStoryboardBasedViewController.m; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | 484BAAEE1BC66FC100D86EBB /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | 48A129811B9BE5E500EF9265 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXFrameworksBuildPhase section */ 103 | 104 | /* Begin PBXGroup section */ 105 | 484BAB2D1BC67ACA00D86EBB /* FDStackView */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 484BAB2E1BC67ACA00D86EBB /* FDGapLayoutGuide.h */, 109 | 484BAB2F1BC67ACA00D86EBB /* FDGapLayoutGuide.m */, 110 | 484BAB301BC67ACA00D86EBB /* FDLayoutSpacer.h */, 111 | 484BAB311BC67ACA00D86EBB /* FDLayoutSpacer.m */, 112 | 484BAB321BC67ACA00D86EBB /* FDStackView.h */, 113 | 484BAB331BC67ACA00D86EBB /* FDStackView.m */, 114 | 484BAB341BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.h */, 115 | 484BAB351BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m */, 116 | 484BAB361BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.h */, 117 | 484BAB371BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m */, 118 | 484BAB381BC67ACA00D86EBB /* FDStackViewExtensions.h */, 119 | 484BAB391BC67ACA00D86EBB /* FDStackViewExtensions.m */, 120 | 484BAB3A1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.h */, 121 | 484BAB3B1BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m */, 122 | 484BAB3C1BC67ACA00D86EBB /* FDTransformLayer.h */, 123 | 484BAB3D1BC67ACA00D86EBB /* FDTransformLayer.m */, 124 | ); 125 | path = FDStackView; 126 | sourceTree = SOURCE_ROOT; 127 | }; 128 | 48A1297B1B9BE5E500EF9265 = { 129 | isa = PBXGroup; 130 | children = ( 131 | 48A129861B9BE5E500EF9265 /* FDStackViewDemo */, 132 | 48A129851B9BE5E500EF9265 /* Products */, 133 | ); 134 | sourceTree = ""; 135 | }; 136 | 48A129851B9BE5E500EF9265 /* Products */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 48A129841B9BE5E500EF9265 /* FDStackViewDemo.app */, 140 | 484BAAF11BC66FC100D86EBB /* libFDStackViewPatch.a */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | 48A129861B9BE5E500EF9265 /* FDStackViewDemo */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 484BAB2D1BC67ACA00D86EBB /* FDStackView */, 149 | 48A1298B1B9BE5E500EF9265 /* AppDelegate.h */, 150 | 48A1298C1B9BE5E500EF9265 /* AppDelegate.m */, 151 | 48A129911B9BE5E500EF9265 /* Main.storyboard */, 152 | E153018C1BA8110C00C857EF /* FDStoryboardBasedViewController.h */, 153 | E153018D1BA8110C00C857EF /* FDStoryboardBasedViewController.m */, 154 | 484BAB661BC8BDBC00D86EBB /* FDCodeBasedViewController.h */, 155 | 484BAB671BC8BDBC00D86EBB /* FDCodeBasedViewController.m */, 156 | 48A129941B9BE5E500EF9265 /* Images.xcassets */, 157 | 48A129961B9BE5E500EF9265 /* LaunchScreen.xib */, 158 | 48A129871B9BE5E500EF9265 /* Supporting Files */, 159 | ); 160 | path = FDStackViewDemo; 161 | sourceTree = ""; 162 | }; 163 | 48A129871B9BE5E500EF9265 /* Supporting Files */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 48A129881B9BE5E500EF9265 /* Info.plist */, 167 | 48A129891B9BE5E500EF9265 /* main.m */, 168 | ); 169 | name = "Supporting Files"; 170 | sourceTree = ""; 171 | }; 172 | /* End PBXGroup section */ 173 | 174 | /* Begin PBXHeadersBuildPhase section */ 175 | 484BAB291BC6740400D86EBB /* Headers */ = { 176 | isa = PBXHeadersBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | 484BAB411BC67ACA00D86EBB /* FDLayoutSpacer.h in Headers */, 180 | 484BAB471BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.h in Headers */, 181 | 484BAB4D1BC67ACA00D86EBB /* FDStackViewExtensions.h in Headers */, 182 | 484BAB531BC67ACA00D86EBB /* FDTransformLayer.h in Headers */, 183 | 484BAB501BC67ACA00D86EBB /* FDStackViewLayoutArrangement.h in Headers */, 184 | 484BAB441BC67ACA00D86EBB /* FDStackView.h in Headers */, 185 | 484BAB4A1BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.h in Headers */, 186 | 484BAB3E1BC67ACA00D86EBB /* FDGapLayoutGuide.h in Headers */, 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | /* End PBXHeadersBuildPhase section */ 191 | 192 | /* Begin PBXNativeTarget section */ 193 | 484BAAF01BC66FC100D86EBB /* FDStackViewPatch */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = 484BAAF71BC66FC100D86EBB /* Build configuration list for PBXNativeTarget "FDStackViewPatch" */; 196 | buildPhases = ( 197 | 484BAAED1BC66FC100D86EBB /* Sources */, 198 | 484BAAEE1BC66FC100D86EBB /* Frameworks */, 199 | 484BAAEF1BC66FC100D86EBB /* CopyFiles */, 200 | 484BAB291BC6740400D86EBB /* Headers */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | ); 206 | name = FDStackViewPatch; 207 | productName = FDStackViewPatch; 208 | productReference = 484BAAF11BC66FC100D86EBB /* libFDStackViewPatch.a */; 209 | productType = "com.apple.product-type.library.static"; 210 | }; 211 | 48A129831B9BE5E500EF9265 /* FDStackViewDemo */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 48A129A71B9BE5E500EF9265 /* Build configuration list for PBXNativeTarget "FDStackViewDemo" */; 214 | buildPhases = ( 215 | 48A129801B9BE5E500EF9265 /* Sources */, 216 | 48A129811B9BE5E500EF9265 /* Frameworks */, 217 | 48A129821B9BE5E500EF9265 /* Resources */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | ); 223 | name = FDStackViewDemo; 224 | productName = FDStackViewDemo; 225 | productReference = 48A129841B9BE5E500EF9265 /* FDStackViewDemo.app */; 226 | productType = "com.apple.product-type.application"; 227 | }; 228 | /* End PBXNativeTarget section */ 229 | 230 | /* Begin PBXProject section */ 231 | 48A1297C1B9BE5E500EF9265 /* Project object */ = { 232 | isa = PBXProject; 233 | attributes = { 234 | KnownAssetTags = ( 235 | demo, 236 | ); 237 | LastSwiftUpdateCheck = 0700; 238 | LastUpgradeCheck = 0700; 239 | ORGANIZATIONNAME = forkingdog; 240 | TargetAttributes = { 241 | 484BAAF01BC66FC100D86EBB = { 242 | CreatedOnToolsVersion = 7.0.1; 243 | }; 244 | 48A129831B9BE5E500EF9265 = { 245 | CreatedOnToolsVersion = 6.4; 246 | DevelopmentTeam = Q3443S8FFW; 247 | }; 248 | }; 249 | }; 250 | buildConfigurationList = 48A1297F1B9BE5E500EF9265 /* Build configuration list for PBXProject "FDStackViewDemo" */; 251 | compatibilityVersion = "Xcode 3.2"; 252 | developmentRegion = English; 253 | hasScannedForEncodings = 0; 254 | knownRegions = ( 255 | en, 256 | Base, 257 | ); 258 | mainGroup = 48A1297B1B9BE5E500EF9265; 259 | productRefGroup = 48A129851B9BE5E500EF9265 /* Products */; 260 | projectDirPath = ""; 261 | projectRoot = ""; 262 | targets = ( 263 | 48A129831B9BE5E500EF9265 /* FDStackViewDemo */, 264 | 484BAAF01BC66FC100D86EBB /* FDStackViewPatch */, 265 | ); 266 | }; 267 | /* End PBXProject section */ 268 | 269 | /* Begin PBXResourcesBuildPhase section */ 270 | 48A129821B9BE5E500EF9265 /* Resources */ = { 271 | isa = PBXResourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 48A129931B9BE5E500EF9265 /* Main.storyboard in Resources */, 275 | 48A129981B9BE5E500EF9265 /* LaunchScreen.xib in Resources */, 276 | 48A129951B9BE5E500EF9265 /* Images.xcassets in Resources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | /* End PBXResourcesBuildPhase section */ 281 | 282 | /* Begin PBXSourcesBuildPhase section */ 283 | 484BAAED1BC66FC100D86EBB /* Sources */ = { 284 | isa = PBXSourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | 484BAB551BC67ACA00D86EBB /* FDTransformLayer.m in Sources */, 288 | 484BAB401BC67ACA00D86EBB /* FDGapLayoutGuide.m in Sources */, 289 | 484BAB461BC67ACA00D86EBB /* FDStackView.m in Sources */, 290 | 484BAB491BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m in Sources */, 291 | 484BAB4C1BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m in Sources */, 292 | 484BAB431BC67ACA00D86EBB /* FDLayoutSpacer.m in Sources */, 293 | 484BAB4F1BC67ACA00D86EBB /* FDStackViewExtensions.m in Sources */, 294 | 484BAB521BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | 48A129801B9BE5E500EF9265 /* Sources */ = { 299 | isa = PBXSourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | 48A1298D1B9BE5E500EF9265 /* AppDelegate.m in Sources */, 303 | 484BAB451BC67ACA00D86EBB /* FDStackView.m in Sources */, 304 | 484BAB4E1BC67ACA00D86EBB /* FDStackViewExtensions.m in Sources */, 305 | 484BAB4B1BC67ACA00D86EBB /* FDStackViewDistributionLayoutArrangement.m in Sources */, 306 | 484BAB511BC67ACA00D86EBB /* FDStackViewLayoutArrangement.m in Sources */, 307 | 48A1298A1B9BE5E500EF9265 /* main.m in Sources */, 308 | 484BAB421BC67ACA00D86EBB /* FDLayoutSpacer.m in Sources */, 309 | 484BAB541BC67ACA00D86EBB /* FDTransformLayer.m in Sources */, 310 | 484BAB481BC67ACA00D86EBB /* FDStackViewAlignmentLayoutArrangement.m in Sources */, 311 | 484BAB681BC8BDBC00D86EBB /* FDCodeBasedViewController.m in Sources */, 312 | E153018E1BA8110C00C857EF /* FDStoryboardBasedViewController.m in Sources */, 313 | 484BAB3F1BC67ACA00D86EBB /* FDGapLayoutGuide.m in Sources */, 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | }; 317 | /* End PBXSourcesBuildPhase section */ 318 | 319 | /* Begin PBXVariantGroup section */ 320 | 48A129911B9BE5E500EF9265 /* Main.storyboard */ = { 321 | isa = PBXVariantGroup; 322 | children = ( 323 | 48A129921B9BE5E500EF9265 /* Base */, 324 | ); 325 | name = Main.storyboard; 326 | sourceTree = ""; 327 | }; 328 | 48A129961B9BE5E500EF9265 /* LaunchScreen.xib */ = { 329 | isa = PBXVariantGroup; 330 | children = ( 331 | 48A129971B9BE5E500EF9265 /* Base */, 332 | ); 333 | name = LaunchScreen.xib; 334 | sourceTree = ""; 335 | }; 336 | /* End PBXVariantGroup section */ 337 | 338 | /* Begin XCBuildConfiguration section */ 339 | 484BAAF81BC66FC100D86EBB /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | DEBUG_INFORMATION_FORMAT = dwarf; 343 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 344 | OTHER_LDFLAGS = "-ObjC"; 345 | PRODUCT_NAME = "$(TARGET_NAME)"; 346 | SKIP_INSTALL = YES; 347 | }; 348 | name = Debug; 349 | }; 350 | 484BAAF91BC66FC100D86EBB /* Release */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 354 | OTHER_LDFLAGS = "-ObjC"; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | SKIP_INSTALL = YES; 357 | }; 358 | name = Release; 359 | }; 360 | 48A129A51B9BE5E500EF9265 /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN_UNREACHABLE_CODE = YES; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | ENABLE_TESTABILITY = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu99; 383 | GCC_DYNAMIC_NO_PIC = NO; 384 | GCC_NO_COMMON_BLOCKS = YES; 385 | GCC_OPTIMIZATION_LEVEL = 0; 386 | GCC_PREPROCESSOR_DEFINITIONS = ( 387 | "DEBUG=1", 388 | "$(inherited)", 389 | ); 390 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 391 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 392 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 393 | GCC_WARN_UNDECLARED_SELECTOR = YES; 394 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 395 | GCC_WARN_UNUSED_FUNCTION = YES; 396 | GCC_WARN_UNUSED_VARIABLE = YES; 397 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 398 | MTL_ENABLE_DEBUG_INFO = YES; 399 | ONLY_ACTIVE_ARCH = YES; 400 | SDKROOT = iphoneos; 401 | }; 402 | name = Debug; 403 | }; 404 | 48A129A61B9BE5E500EF9265 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 409 | CLANG_CXX_LIBRARY = "libc++"; 410 | CLANG_ENABLE_MODULES = YES; 411 | CLANG_ENABLE_OBJC_ARC = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_CONSTANT_CONVERSION = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 419 | CLANG_WARN_UNREACHABLE_CODE = YES; 420 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 421 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 422 | COPY_PHASE_STRIP = NO; 423 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 424 | ENABLE_NS_ASSERTIONS = NO; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | GCC_C_LANGUAGE_STANDARD = gnu99; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 435 | MTL_ENABLE_DEBUG_INFO = NO; 436 | SDKROOT = iphoneos; 437 | VALIDATE_PRODUCT = YES; 438 | }; 439 | name = Release; 440 | }; 441 | 48A129A81B9BE5E500EF9265 /* Debug */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 445 | CLANG_ENABLE_MODULES = YES; 446 | CODE_SIGN_IDENTITY = "iPhone Developer"; 447 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 448 | INFOPLIST_FILE = FDStackViewDemo/Info.plist; 449 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 450 | PRODUCT_BUNDLE_IDENTIFIER = "com.forkingdog.$(PRODUCT_NAME:rfc1034identifier)"; 451 | PRODUCT_NAME = "$(TARGET_NAME)"; 452 | PROVISIONING_PROFILE = "f3885f66-1ec2-4718-b197-0dc5422cb085"; 453 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 454 | }; 455 | name = Debug; 456 | }; 457 | 48A129A91B9BE5E500EF9265 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | CLANG_ENABLE_MODULES = YES; 462 | CODE_SIGN_IDENTITY = "iPhone Developer"; 463 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 464 | INFOPLIST_FILE = FDStackViewDemo/Info.plist; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = "com.forkingdog.$(PRODUCT_NAME:rfc1034identifier)"; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | PROVISIONING_PROFILE = "f3885f66-1ec2-4718-b197-0dc5422cb085"; 469 | }; 470 | name = Release; 471 | }; 472 | /* End XCBuildConfiguration section */ 473 | 474 | /* Begin XCConfigurationList section */ 475 | 484BAAF71BC66FC100D86EBB /* Build configuration list for PBXNativeTarget "FDStackViewPatch" */ = { 476 | isa = XCConfigurationList; 477 | buildConfigurations = ( 478 | 484BAAF81BC66FC100D86EBB /* Debug */, 479 | 484BAAF91BC66FC100D86EBB /* Release */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | 48A1297F1B9BE5E500EF9265 /* Build configuration list for PBXProject "FDStackViewDemo" */ = { 485 | isa = XCConfigurationList; 486 | buildConfigurations = ( 487 | 48A129A51B9BE5E500EF9265 /* Debug */, 488 | 48A129A61B9BE5E500EF9265 /* Release */, 489 | ); 490 | defaultConfigurationIsVisible = 0; 491 | defaultConfigurationName = Release; 492 | }; 493 | 48A129A71B9BE5E500EF9265 /* Build configuration list for PBXNativeTarget "FDStackViewDemo" */ = { 494 | isa = XCConfigurationList; 495 | buildConfigurations = ( 496 | 48A129A81B9BE5E500EF9265 /* Debug */, 497 | 48A129A91B9BE5E500EF9265 /* Release */, 498 | ); 499 | defaultConfigurationIsVisible = 0; 500 | defaultConfigurationName = Release; 501 | }; 502 | /* End XCConfigurationList section */ 503 | }; 504 | rootObject = 48A1297C1B9BE5E500EF9265 /* Project object */; 505 | } 506 | -------------------------------------------------------------------------------- /FDStackViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FDStackViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/9/6. 6 | // Copyright (c) 2015年 forkingdog. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /FDStackViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/9/6. 6 | // Copyright (c) 2015年 forkingdog. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | - (void)applicationWillResignActive:(UIApplication *)application { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | - (void)applicationDidEnterBackground:(UIApplication *)application { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | - (void)applicationWillEnterForeground:(UIApplication *)application { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | - (void)applicationWillTerminate:(UIApplication *)application { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /FDStackViewDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /FDStackViewDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 107 | 108 | 109 | 110 | 111 | 119 | 127 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 360 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 393 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /FDStackViewDemo/FDCodeBasedViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FDCodeBasedViewController.h 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/10/10. 6 | // Copyright © 2015年 forkingdog. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FDCodeBasedViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FDStackViewDemo/FDCodeBasedViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FDCodeBasedViewController.m 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/10/10. 6 | // Copyright © 2015年 forkingdog. All rights reserved. 7 | // 8 | 9 | #import "FDCodeBasedViewController.h" 10 | 11 | @implementation FDCodeBasedViewController 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | 16 | UILabel *forkingLabel = [[UILabel alloc] init]; 17 | forkingLabel.text = @"forking"; 18 | UIImageView *logoImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"forkingdog"]]; 19 | UILabel *dogLabel = [[UILabel alloc] init]; 20 | dogLabel.text = @"dog"; 21 | 22 | UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[forkingLabel, logoImageView, dogLabel]]; 23 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 24 | stackView.axis = UILayoutConstraintAxisHorizontal; 25 | stackView.distribution = UIStackViewDistributionFill; 26 | stackView.alignment = UIStackViewAlignmentCenter; 27 | [self.view addSubview:stackView]; 28 | 29 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:stackView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 30 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:stackView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /FDStackViewDemo/FDStoryboardBasedViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FDStoryboardBasedViewController.h 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/9/15. 6 | // Copyright © 2015 forkingdog. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FDStoryboardBasedViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FDStackViewDemo/FDStoryboardBasedViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FDStoryboardBasedViewController.m 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/9/15. 6 | // Copyright © 2015 forkingdog. All rights reserved. 7 | // 8 | 9 | #import "FDStoryboardBasedViewController.h" 10 | 11 | @interface FDStoryboardBasedViewController () 12 | @property (nonatomic, weak) IBOutlet UIStackView *stackView; 13 | @property (nonatomic, weak) IBOutlet UIImageView *forkingdogImageView; 14 | @property (nonatomic, weak) IBOutlet UISegmentedControl *axisSegmentControl; 15 | @property (nonatomic, weak) IBOutlet UISegmentedControl *alignmentSegmentControl; 16 | @property (nonatomic, weak) IBOutlet UISegmentedControl *distributionSegmentControl; 17 | @property (nonatomic, weak) IBOutlet UISlider *spacingSlider; 18 | @property (nonatomic, weak) IBOutlet UILabel *spacingLabel; 19 | @property (nonatomic, strong) NSMutableArray *mutableAddingViews; 20 | @end 21 | 22 | @implementation FDStoryboardBasedViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | [self updateFromStackViewAttributes]; 27 | } 28 | 29 | - (void)updateFromStackViewAttributes { 30 | self.axisSegmentControl.selectedSegmentIndex = self.stackView.axis; 31 | self.alignmentSegmentControl.selectedSegmentIndex = self.stackView.alignment; 32 | self.distributionSegmentControl.selectedSegmentIndex = self.stackView.distribution; 33 | self.spacingLabel.text = [NSString stringWithFormat:@"Spacing (%.1lf)", self.stackView.spacing]; 34 | CGSize headerSize = [self.tableView.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 35 | self.tableView.tableHeaderView.frame = CGRectMake(0, 0, headerSize.width ?: CGRectGetWidth(self.view.frame), headerSize.height); 36 | self.tableView.tableHeaderView = self.tableView.tableHeaderView; 37 | } 38 | 39 | #pragma mark - Actions 40 | 41 | - (IBAction)axisSegmentControlValueChangedAction:(UISegmentedControl *)sender { 42 | self.stackView.axis = sender.selectedSegmentIndex; 43 | [self updateFromStackViewAttributes]; 44 | } 45 | 46 | - (IBAction)alignmentSegmentControlValueChangedAction:(UISegmentedControl *)sender { 47 | self.stackView.alignment = sender.selectedSegmentIndex; 48 | [self updateFromStackViewAttributes]; 49 | } 50 | 51 | - (IBAction)distributionSegmentControlValueChangedAction:(UISegmentedControl *)sender { 52 | self.stackView.distribution = sender.selectedSegmentIndex; 53 | [self updateFromStackViewAttributes]; 54 | } 55 | 56 | - (IBAction)spacingSliderValueChangedAction:(UISlider *)sender { 57 | self.stackView.spacing = sender.value; 58 | [self updateFromStackViewAttributes]; 59 | } 60 | 61 | - (IBAction)hideAction:(id)sender { 62 | self.forkingdogImageView.hidden ^= 1; 63 | } 64 | 65 | - (IBAction)addAction:(id)sender { 66 | if (!self.mutableAddingViews) { 67 | self.mutableAddingViews = @[].mutableCopy; 68 | } 69 | UILabel *label = [[UILabel alloc] init]; 70 | label.font = [UIFont systemFontOfSize:25]; 71 | label.backgroundColor = [UIColor colorWithWhite:arc4random_uniform(255) / 255.0 alpha:1]; 72 | label.text = @(self.mutableAddingViews.count).stringValue; 73 | [self.stackView addArrangedSubview:label]; 74 | 75 | [self.mutableAddingViews addObject:label]; 76 | [self updateFromStackViewAttributes]; 77 | } 78 | 79 | - (IBAction)removeAction:(id)sender { 80 | if (self.mutableAddingViews.count == 0) { 81 | return; 82 | } 83 | UILabel *label = self.mutableAddingViews.lastObject; 84 | [label removeFromSuperview]; 85 | [self.mutableAddingViews removeObject:label]; 86 | [self updateFromStackViewAttributes]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /FDStackViewDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /FDStackViewDemo/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /FDStackViewDemo/Images.xcassets/forkingdog.imageset/11923583.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forkingdog/FDStackView/d507a47d185de1cb0cdd35cdeb28fcdd827d9859/FDStackViewDemo/Images.xcassets/forkingdog.imageset/11923583.png -------------------------------------------------------------------------------- /FDStackViewDemo/Images.xcassets/forkingdog.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "11923583.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /FDStackViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /FDStackViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FDStackViewDemo 4 | // 5 | // Created by sunnyxx on 15/9/6. 6 | // Copyright (c) 2015年 forkingdog. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FDStackView 2 | 3 | Use UIStackView as if it supports iOS6. 4 | ![forkingdog](https://cloud.githubusercontent.com/assets/219689/7244961/4209de32-e816-11e4-87bc-b161c442d348.png) 5 | 6 | # Problem 7 | 8 | UIStackView is a very handy tool to build flow layout, but it's available only when iOS9+, we've found some great compatible replacements like OAStackView, but we want more: 9 | 10 | - **Perfect downward compatible**, no infectivity, use UIStackView **directly** as if it's shipped from iOS6. 11 | - **Interface builder support**, live preview. 12 | - Keep layout constraints as closely as UIStackView constructs. 13 | 14 | # Usage 15 | 16 | #### Podfile 17 | ```ruby 18 | platform :ios, '7.0' 19 | pod "FDStackView", "1.0" 20 | ``` 21 | 22 | **Import nothing, learn nothing, it just works.** 23 | 24 | - It will automatically replace the symbol for UIStackView into FDStackView at runtime before iOS9. 25 | 26 | ``` objc 27 | // Works in iOS6+, use it directly. 28 | UIStackView *stackView = [[UIStackView alloc] init]; 29 | stackView.axis = UILayoutConstraintAxisHorizontal; 30 | stackView.distribution = UIStackViewDistributionFill; 31 | stackView.alignment = UIStackViewAlignmentTop; 32 | [stackView addArrangedSubview:[[UILabel alloc] init]]; 33 | [self.view addSubview:stackView]; 34 | ``` 35 | 36 | - Interface Builder Support 37 | 38 | Set `Builds for` option to `iOS 9.0 and later` to eliminate the version error in Xcode: 39 | 40 | ![How to use in IB](https://raw.githubusercontent.com/forkingdog/FDStackView/master/Snapshots/snapshot0.png) 41 | 42 | Now, use UIStackView as you like and its reactive options and live preview: 43 | 44 | ![UIStackView preview in IB](https://raw.githubusercontent.com/forkingdog/FDStackView/master/Snapshots/snapshot1.png) 45 | 46 | # Requirements 47 | 48 | - Xcode 7+ (For interface builder supports and the latest Objective-C Syntax) 49 | - Base SDK iOS 9.0+ (To link UIStackView symbol in UIKit) 50 | 51 | # Versions 52 | 53 | - 1.0.1 is the lastest version. We released it after we have used it in our official application. And it was successfully passed through the App Store's review. So you have no concern to use it. 54 | 55 | # License 56 | 57 | The MIT License (MIT) 58 | 59 | Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 60 | 61 | Permission is hereby granted, free of charge, to any person obtaining a copy 62 | of this software and associated documentation files (the "Software"), to deal 63 | in the Software without restriction, including without limitation the rights 64 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 65 | copies of the Software, and to permit persons to whom the Software is 66 | furnished to do so, subject to the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be included in all 69 | copies or substantial portions of the Software. 70 | 71 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 72 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 73 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 74 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 75 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 76 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 77 | SOFTWARE. 78 | 79 | -------------------------------------------------------------------------------- /Snapshots/snapshot0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forkingdog/FDStackView/d507a47d185de1cb0cdd35cdeb28fcdd827d9859/Snapshots/snapshot0.png -------------------------------------------------------------------------------- /Snapshots/snapshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forkingdog/FDStackView/d507a47d185de1cb0cdd35cdeb28fcdd827d9859/Snapshots/snapshot1.png --------------------------------------------------------------------------------