diff --git a/Polyfill/UIStackView+TZStackView.h b/Polyfill/UIStackView+TZStackView.h new file mode 100644 index 0000000..fc18b61 --- /dev/null +++ b/Polyfill/UIStackView+TZStackView.h @@ -0,0 +1,100 @@ +// +// UIStackView+TZStackView.h +// +// Created by Frederic Barthelemy on 9/9/15. +// + +#import + +// You probably want the real UIStackView! +#import + +// Define a preprocessor macro to coopt all your references to UIStackView with this class. +#define UIStackView _polyfill_UIStackView + +/** + This class is a polyfill. Go look at UIStackView.h or TZStackView.swift for implementation details. + + Usage: Include this in your Prefix.pch, and use UIStackView * everywhere as if you were on iOS 9.0 + + This class works by overriding +(instancetype)alloc to dynamically alloc the appropriate class. + Additionally it defines a PreProcessor macro that will replace all your uses of UIStackView with this class. + + @warning: Dynamic tricks like NSStringFromClass([UIStackView class]) will probably not do what you want. Also, the only supported class method is `alloc`. + + All methods defined here are simply stubs to silence the compiler, since you can't ever alloc an instance of this. + */ +@interface _polyfill_UIStackView : UIView + +#pragma mark - Everything below here is copied in from UIStackView.h - + +/* UIStackView enforces that all views in the arrangedSubviews list + must be subviews of the UIStackView. + Thus, when a view is added to the arrangedSubviews, UIStackView + adds it as a subview if it isn't already. And when a view in a + UIStackView's arrangedSubviews list receives -removeFromSuperview + it is also removed from the arrangedSubviews. + */ +- (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views; // Adds views as subviews of the receiver. +@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *arrangedSubviews; + +/* Add a view to the end of the arrangedSubviews list. + Maintains the rule that the arrangedSubviews list is a subset of the + subviews list by adding the view as a subview of the receiver if + necessary. + Does not affect the subview ordering if view is already a subview + of the receiver. + */ +- (void)addArrangedSubview:(UIView *)view; + +/* Removes a subview from the list of arranged subviews without removing it as + a subview of the receiver. + To remove the view as a subview, send it -removeFromSuperview as usual; + the relevant UIStackView will remove it from its arrangedSubviews list + automatically. + */ +- (void)removeArrangedSubview:(UIView *)view; +/* + Adds the view as a subview of the container if it isn't already. + Updates the stack index (but not the subview index) of the + arranged subview if it's already in the arrangedSubviews list. + */ +- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex; + +/* A stack with a horizontal axis is a row of arrangedSubviews, + and a stack with a vertical axis is a column of arrangedSubviews. + */ +@property(nonatomic) UILayoutConstraintAxis axis; + +/* The layout of the arrangedSubviews along the axis + */ +@property(nonatomic) UIStackViewDistribution distribution; + +/* The layout of the arrangedSubviews transverse to the axis; + e.g., leading/trailing edges in a vertical stack + */ +@property(nonatomic) UIStackViewAlignment alignment; + +/* Spacing between adjacent edges of arrangedSubviews. + Used as a strict spacing for the Fill distributions, and + as a minimum spacing for the EqualCentering and EqualSpacing + distributions. Use negative values to allow overlap. + */ +@property(nonatomic) CGFloat spacing; + +/* Baseline-to-baseline spacing in vertical stacks. + The baselineRelativeArrangement property supports specifications of vertical + space from the last baseline of one text-based view to the first baseline of a + text-based view below, or from the top (or bottom) of a container to the first + (or last) baseline of a contained text-based view. + This property is ignored in horizontal stacks. Use the alignment property + to specify baseline alignment in horizontal stacks. + Defaults to NO. + */ +@property(nonatomic,getter=isBaselineRelativeArrangement) BOOL baselineRelativeArrangement; + +/* Uses margin layout attributes for edge constraints where applicable. + Defaults to NO. + */ +@property(nonatomic,getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement; +@end diff --git a/Polyfill/UIStackView+TZStackView.m b/Polyfill/UIStackView+TZStackView.m new file mode 100644 index 0000000..ad30ab6 --- /dev/null +++ b/Polyfill/UIStackView+TZStackView.m @@ -0,0 +1,28 @@ +// +// UIStackView+TZStackView.m +// +// Created by Frederic Barthelemy on 9/9/15. +// + +#import + +#import "FitStar-Swift.h" +#import "UIStackView+TZStackView.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0 +#error UIStackView+TZStackView Polyfill is no longer required! Remove these files. Congratulations! +#endif + +@implementation _polyfill_UIStackView + ++ (instancetype)alloc { + // Actually pick the best StackView implementation: + return [NSClassFromString(@"UIStackView") alloc] ?: [TZStackView alloc]; +} + +#pragma mark - Polyfill Stubs +- (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object."); +- (void)addArrangedSubview:(UIView *)view FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object."); +- (void)removeArrangedSubview:(UIView *)view FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object."); +- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object."); +@end diff --git a/README.md b/README.md index c604c20..724dedb 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A wonderful layout component called the [`UIStackView` was introduced with *iOS - ✅ Compatible with **iOS 7.x** and **iOS 8.x** - ✅ Supports the complete API of `UIStackView` including **all** *distribution* and *alignment* options - ✅ Supports animating the `hidden` property of the *arranged subviews* +- ✅ Optional [Polyfill](#polyfill) for Objective-C code to use the name UIStackView, while dynamically selecting TZStackView when on earlier iOS versions. - ❌ Supports *Storyboard* So this implementation does **not** support Storyboard. It is meant for iOS developers who, like me, want to use the `UIStackView` in our existing apps and like to layout their components in code as opposed to using *Storyboard*. @@ -67,6 +68,22 @@ UIView.animateWithDuration(0.5, animations: { ``` ![TZStackView hidden animation example](/assets/TZStackView-hide-animation.gif) +## Polyfill +(Objective-C only) + +If you're writing code with Objective-C, and you want to dynamically select UIStackView over TZStackView when on iOS versions that provide it, check out [UIStackView+TZStackView](./Polyfill/) + +If you include these files in your project, and include the header in your Prefix.pch, along with TZStackView [see Setup](#setup) you to will be able to type `UIStackView` and have the code dynamically select the right class to fulfill your needs. + +If you go down this route, you don't have to do anything to migrate to UIStackView, but to remove TZStackView. + +Example: +```objc +UIStackView * stack = [[UIStackView alloc] initWithArrangedSubviews:@[/*…*/]]; +```` + +More documentation in [UIStackView+TZStackView.h](./Polyfill/TZStackView.h) + ## Migrating to UIStackView If at a later point you decide to make *iOS 9* the minimum requirement of your app (it will happen sooner or later), you will want to migrate to the real `UIStackView` instead of using this implementation. Because the `TZStackView` is a drop-in replacement for `UIStackView`, you simply replace: