Skip to content

Commit ab8fb9b

Browse files
committed
Add a polyfill for Objective-C to select TZStackView only when UIStackView is unavailable.
1 parent 9b6b315 commit ab8fb9b

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

Polyfill/UIStackView+TZStackView.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//
2+
// UIStackView+TZStackView.h
3+
// FitStar
4+
//
5+
// Created by Frederic Barthelemy on 9/9/15.
6+
// Copyright © 2015 FitStar. All rights reserved.
7+
//
8+
9+
#import <UIKit/UIKit.h>
10+
11+
// You probably want the real UIStackView!
12+
#import <UIKit/UIStackView.h>
13+
14+
// Define a preprocessor macro to coopt all your references to UIStackView with this class.
15+
#define UIStackView _polyfill_UIStackView
16+
17+
/**
18+
This class is a polyfill. Go look at UIStackView.h or TZStackView.swift for implementation details.
19+
20+
Usage: Include this in your Prefix.pch, and use UIStackView * everywhere as if you were on iOS 9.0
21+
22+
This class works by overriding +(instancetype)alloc to dynamically alloc the appropriate class.
23+
Additionally it defines a PreProcessor macro that will replace all your uses of UIStackView with this class.
24+
25+
@warning: Dynamic tricks like NSStringFromClass([UIStackView class]) will probably not do what you want. Also, the only supported class method is `alloc`.
26+
27+
All methods defined here are simply stubs to silence the compiler, since you can't ever alloc an instance of this.
28+
*/
29+
@interface _polyfill_UIStackView : UIView
30+
31+
#pragma mark - Everything below here is copied in from UIStackView.h -
32+
33+
/* UIStackView enforces that all views in the arrangedSubviews list
34+
must be subviews of the UIStackView.
35+
Thus, when a view is added to the arrangedSubviews, UIStackView
36+
adds it as a subview if it isn't already. And when a view in a
37+
UIStackView's arrangedSubviews list receives -removeFromSuperview
38+
it is also removed from the arrangedSubviews.
39+
*/
40+
- (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views; // Adds views as subviews of the receiver.
41+
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *arrangedSubviews;
42+
43+
/* Add a view to the end of the arrangedSubviews list.
44+
Maintains the rule that the arrangedSubviews list is a subset of the
45+
subviews list by adding the view as a subview of the receiver if
46+
necessary.
47+
Does not affect the subview ordering if view is already a subview
48+
of the receiver.
49+
*/
50+
- (void)addArrangedSubview:(UIView *)view;
51+
52+
/* Removes a subview from the list of arranged subviews without removing it as
53+
a subview of the receiver.
54+
To remove the view as a subview, send it -removeFromSuperview as usual;
55+
the relevant UIStackView will remove it from its arrangedSubviews list
56+
automatically.
57+
*/
58+
- (void)removeArrangedSubview:(UIView *)view;
59+
/*
60+
Adds the view as a subview of the container if it isn't already.
61+
Updates the stack index (but not the subview index) of the
62+
arranged subview if it's already in the arrangedSubviews list.
63+
*/
64+
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex;
65+
66+
/* A stack with a horizontal axis is a row of arrangedSubviews,
67+
and a stack with a vertical axis is a column of arrangedSubviews.
68+
*/
69+
@property(nonatomic) UILayoutConstraintAxis axis;
70+
71+
/* The layout of the arrangedSubviews along the axis
72+
*/
73+
@property(nonatomic) UIStackViewDistribution distribution;
74+
75+
/* The layout of the arrangedSubviews transverse to the axis;
76+
e.g., leading/trailing edges in a vertical stack
77+
*/
78+
@property(nonatomic) UIStackViewAlignment alignment;
79+
80+
/* Spacing between adjacent edges of arrangedSubviews.
81+
Used as a strict spacing for the Fill distributions, and
82+
as a minimum spacing for the EqualCentering and EqualSpacing
83+
distributions. Use negative values to allow overlap.
84+
*/
85+
@property(nonatomic) CGFloat spacing;
86+
87+
/* Baseline-to-baseline spacing in vertical stacks.
88+
The baselineRelativeArrangement property supports specifications of vertical
89+
space from the last baseline of one text-based view to the first baseline of a
90+
text-based view below, or from the top (or bottom) of a container to the first
91+
(or last) baseline of a contained text-based view.
92+
This property is ignored in horizontal stacks. Use the alignment property
93+
to specify baseline alignment in horizontal stacks.
94+
Defaults to NO.
95+
*/
96+
@property(nonatomic,getter=isBaselineRelativeArrangement) BOOL baselineRelativeArrangement;
97+
98+
/* Uses margin layout attributes for edge constraints where applicable.
99+
Defaults to NO.
100+
*/
101+
@property(nonatomic,getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement;
102+
@end

Polyfill/UIStackView+TZStackView.m

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// UIStackView+TZStackView.m
3+
// FitStar
4+
//
5+
// Created by Frederic Barthelemy on 9/9/15.
6+
// Copyright © 2015 FitStar. All rights reserved.
7+
//
8+
9+
#import <objc/runtime.h>
10+
11+
#import "FitStar-Swift.h"
12+
#import "UIStackView+TZStackView.h"
13+
14+
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
15+
#error UIStackView+TZStackView Polyfill is no longer required! Remove these files. Congratulations!
16+
#endif
17+
18+
@implementation _polyfill_UIStackView
19+
20+
+ (instancetype)alloc {
21+
// Actually pick the best StackView implementation:
22+
return [NSClassFromString(@"UIStackView") alloc] ?: [TZStackView alloc];
23+
}
24+
25+
#pragma mark - Polyfill Stubs
26+
- (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object.");
27+
- (void)addArrangedSubview:(UIView *)view FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object.");
28+
- (void)removeArrangedSubview:(UIView *)view FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object.");
29+
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex FIT_UNAVAILABLE(@"Polyfill Stub",@"You can't actually have an instance of this object.");
30+
@end

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ A wonderful layout component called the [`UIStackView` was introduced with *iOS
77
- ✅ Compatible with **iOS 7.x** and **iOS 8.x**
88
- ✅ Supports the complete API of `UIStackView` including **all** *distribution* and *alignment* options
99
- ✅ Supports animating the `hidden` property of the *arranged subviews*
10+
- ✅ Optional [Polyfill](#polyfill) for Objective-C code to use the name UIStackView, while dynamically selecting TZStackView when on earlier iOS versions.
1011
- ❌ Supports *Storyboard*
1112

1213
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: {
6768
```
6869
![TZStackView hidden animation example](/assets/TZStackView-hide-animation.gif)
6970

71+
## Polyfill
72+
(Objective-C only)
73+
74+
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/)
75+
76+
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.
77+
78+
If you go down this route, you don't have to do anything to migrate to UIStackView, but to remove TZStackView.
79+
80+
Example:
81+
```objc
82+
UIStackView * stack = [[UIStackView alloc] initWithArrangedSubviews:@[/**/]];
83+
````
84+
85+
More documentation in [UIStackView+TZStackView.h](./Polyfill/TZStackView.h)
86+
7087
## Migrating to UIStackView
7188
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:
7289

0 commit comments

Comments
 (0)