Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions Polyfill/UIStackView+TZStackView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// UIStackView+TZStackView.h
//
// Created by Frederic Barthelemy on 9/9/15.
//

#import <UIKit/UIKit.h>

// You probably want the real UIStackView!
#import <UIKit/UIStackView.h>

// 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
28 changes: 28 additions & 0 deletions Polyfill/UIStackView+TZStackView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// UIStackView+TZStackView.m
//
// Created by Frederic Barthelemy on 9/9/15.
//

#import <objc/runtime.h>

#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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*.
Expand Down Expand Up @@ -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:

Expand Down