Browse Source

Merge branch 'v2' into newAPI

Daniel Zlotin 6 years ago
parent
commit
96134c4605
65 changed files with 2162 additions and 659 deletions
  1. 1
    1
      README.md
  2. 0
    12
      lib/android/app/src/main/java/com/reactnativenavigation/parse/NavigationOptions.java
  3. 14
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  4. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  5. 262
    0
      lib/ios/HMSegmentedControl.h
  6. 958
    0
      lib/ios/HMSegmentedControl.m
  7. 2
    8
      lib/ios/RNNBottomTabOptions.h
  8. 51
    0
      lib/ios/RNNBottomTabOptions.m
  9. 2
    9
      lib/ios/RNNBottomTabsOptions.h
  10. 45
    0
      lib/ios/RNNBottomTabsOptions.m
  11. 10
    0
      lib/ios/RNNBridgeManager.h
  12. 85
    0
      lib/ios/RNNBridgeManager.m
  13. 1
    1
      lib/ios/RNNCommandsHandler.h
  14. 3
    7
      lib/ios/RNNCommandsHandler.m
  15. 4
    2
      lib/ios/RNNControllerFactory.h
  16. 41
    13
      lib/ios/RNNControllerFactory.m
  17. 2
    0
      lib/ios/RNNLayoutNode.h
  18. 8
    0
      lib/ios/RNNLayoutNode.m
  19. 0
    1
      lib/ios/RNNNavigationButtons.h
  20. 14
    15
      lib/ios/RNNNavigationButtons.m
  21. 2
    1
      lib/ios/RNNNavigationController.h
  22. 4
    0
      lib/ios/RNNNavigationController.m
  23. 12
    18
      lib/ios/RNNNavigationOptions.h
  24. 13
    243
      lib/ios/RNNNavigationOptions.m
  25. 2
    2
      lib/ios/RNNNavigationStackManager.h
  26. 5
    6
      lib/ios/RNNNavigationStackManager.m
  27. 17
    0
      lib/ios/RNNOptions.h
  28. 18
    0
      lib/ios/RNNOptions.m
  29. 8
    3
      lib/ios/RNNRootViewController.h
  30. 6
    11
      lib/ios/RNNRootViewController.m
  31. 11
    0
      lib/ios/RNNRootViewProtocol.h
  32. 8
    0
      lib/ios/RNNSegmentedControl.h
  33. 11
    0
      lib/ios/RNNSegmentedControl.m
  34. 2
    1
      lib/ios/RNNSideMenuChildVC.h
  35. 4
    0
      lib/ios/RNNSideMenuChildVC.m
  36. 2
    1
      lib/ios/RNNSideMenuController.h
  37. 4
    0
      lib/ios/RNNSideMenuController.m
  38. 2
    8
      lib/ios/RNNSideMenuOptions.h
  39. 22
    10
      lib/ios/RNNSideMenuOptions.m
  40. 2
    1
      lib/ios/RNNTabBarController.h
  41. 4
    0
      lib/ios/RNNTabBarController.m
  42. 0
    32
      lib/ios/RNNTabBarOptions.m
  43. 0
    32
      lib/ios/RNNTabItemOptions.m
  44. 4
    8
      lib/ios/RNNTopBarOptions.h
  45. 164
    28
      lib/ios/RNNTopBarOptions.m
  46. 9
    0
      lib/ios/RNNTopTabOptions.h
  47. 18
    0
      lib/ios/RNNTopTabOptions.m
  48. 7
    0
      lib/ios/RNNTopTabsOptions.h
  49. 10
    0
      lib/ios/RNNTopTabsOptions.m
  50. 14
    0
      lib/ios/RNNTopTabsViewController.h
  51. 79
    0
      lib/ios/RNNTopTabsViewController.m
  52. 0
    2
      lib/ios/ReactNativeNavigation.h
  53. 8
    80
      lib/ios/ReactNativeNavigation.m
  54. 98
    20
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  55. 2
    4
      lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m
  56. 1
    1
      lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m
  57. 3
    3
      lib/ios/ReactNativeNavigationTests/RNNNavigationOptionsTest.m
  58. 5
    5
      lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m
  59. 2
    0
      lib/src/params/containers/Container.js
  60. 0
    5
      lib/src/params/options/Options.js
  61. 0
    20
      lib/src/params/options/Options.test.js
  62. 6
    0
      lib/src/params/options/TopBar.js
  63. 22
    1
      lib/src/params/options/TopBar.test.js
  64. 46
    42
      playground/src/containers/OptionsScreen.js
  65. 1
    1
      playground/src/containers/WelcomeScreen.js

+ 1
- 1
README.md View File

@@ -13,7 +13,7 @@ We are rebuilding react-native-navigation
13 13
 ## Why Rebuild react-native-navigation?
14 14
 
15 15
 ### A New & Improved Core Architecture
16
-react-native-navigation has a few issues which are unsolvable in its current architecture. These issues stem from the same problem: you cannot specify on which screen you wish to make an action. Whenever you want to push a screen, show a modal or any other action, the action defaults to originate from your current screen. In most cases this is fine, but becoms problematic in specific edge cases. For example: <br>
16
+react-native-navigation has a few issues which are unsolvable in its current architecture. These issues stem from the same problem: you cannot specify on which screen you wish to make an action. Whenever you want to push a screen, show a modal or any other action, the action defaults to originate from your current screen. In most cases this is fine, but becomes problematic in specific edge cases. For example: <br>
17 17
 * What if you want to update your navbar icons and the user pops the screen? Your icons might update on the wrong screen.
18 18
 * What if you want to push a screen as a result of a redux action?
19 19
 

+ 0
- 12
lib/android/app/src/main/java/com/reactnativenavigation/parse/NavigationOptions.java View File

@@ -37,8 +37,6 @@ public class NavigationOptions implements DEFAULT_VALUES {
37 37
 		result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
38 38
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
39 39
 		result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
40
-		result.rightButtons = Button.parseJsonArray(json.optJSONArray("rightButtons"));
41
-        result.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"));
42 40
 
43 41
 		return result.withDefaultOptions(defaultOptions);
44 42
 	}
@@ -47,21 +45,11 @@ public class NavigationOptions implements DEFAULT_VALUES {
47 45
     @NonNull public TopTabsOptions topTabsOptions = new TopTabsOptions();
48 46
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
49 47
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
50
-    public ArrayList<Button> leftButtons;
51
-    public ArrayList<Button> rightButtons;
52 48
 
53 49
 	public void mergeWith(final NavigationOptions other) {
54 50
         topBarOptions.mergeWith(other.topBarOptions);
55 51
         topTabsOptions.mergeWith(other.topTabsOptions);
56 52
         bottomTabsOptions.mergeWith(other.bottomTabsOptions);
57
-
58
-        if(other.leftButtons != null) {
59
-            leftButtons = other.leftButtons;
60
-        }
61
-
62
-        if(other.rightButtons != null) {
63
-            rightButtons = other.rightButtons;
64
-        }
65 53
     }
66 54
 
67 55
     NavigationOptions withDefaultOptions(final NavigationOptions other) {

+ 14
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java View File

@@ -9,6 +9,8 @@ import com.reactnativenavigation.utils.TypefaceLoader;
9 9
 
10 10
 import org.json.JSONObject;
11 11
 
12
+import java.util.ArrayList;
13
+
12 14
 public class TopBarOptions implements DEFAULT_VALUES {
13 15
 
14 16
 	public static TopBarOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
@@ -22,6 +24,8 @@ public class TopBarOptions implements DEFAULT_VALUES {
22 24
 		options.textFontFamily = typefaceManager.getTypeFace(json.optString("textFontFamily", NO_VALUE));
23 25
 		options.hidden = NavigationOptions.BooleanOptions.parse(json.optString("hidden"));
24 26
 		options.animateHide = NavigationOptions.BooleanOptions.parse(json.optString("animateHide"));
27
+		options.rightButtons = Button.parseJsonArray(json.optJSONArray("rightButtons"));
28
+		options.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"));
25 29
 
26 30
 		return options;
27 31
 	}
@@ -33,6 +37,8 @@ public class TopBarOptions implements DEFAULT_VALUES {
33 37
 	@Nullable public Typeface textFontFamily;
34 38
 	public NavigationOptions.BooleanOptions hidden = NavigationOptions.BooleanOptions.False;
35 39
 	public NavigationOptions.BooleanOptions animateHide = NavigationOptions.BooleanOptions.False;
40
+	public ArrayList<Button> leftButtons;
41
+	public ArrayList<Button> rightButtons;
36 42
 
37 43
 	void mergeWith(final TopBarOptions other) {
38 44
 		if (!NO_VALUE.equals(other.title)) title = other.title;
@@ -48,6 +54,10 @@ public class TopBarOptions implements DEFAULT_VALUES {
48 54
 			hidden = other.hidden;
49 55
 		if (other.animateHide != NavigationOptions.BooleanOptions.NoValue)
50 56
 			animateHide = other.animateHide;
57
+		if(other.leftButtons != null)
58
+			leftButtons = other.leftButtons;
59
+		if(other.rightButtons != null)
60
+			rightButtons = other.rightButtons;
51 61
 	}
52 62
 
53 63
     void mergeWithDefault(TopBarOptions defaultOptions) {
@@ -65,5 +75,9 @@ public class TopBarOptions implements DEFAULT_VALUES {
65 75
             hidden = defaultOptions.hidden;
66 76
         if (animateHide == NavigationOptions.BooleanOptions.NoValue)
67 77
             animateHide = defaultOptions.animateHide;
78
+		if(leftButtons == null)
79
+			leftButtons = defaultOptions.leftButtons;
80
+		if(rightButtons == null)
81
+			rightButtons = defaultOptions.rightButtons;
68 82
     }
69 83
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java View File

@@ -26,7 +26,7 @@ public class OptionsPresenter {
26 26
 
27 27
     public void applyOptions(NavigationOptions options) {
28 28
         applyTopBarOptions(options.topBarOptions);
29
-        applyButtons(options.leftButtons, options.rightButtons);
29
+        applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
30 30
         applyTopTabsOptions(options.topTabsOptions);
31 31
         applyTopTabOptions(options.topTabOptions);
32 32
     }

+ 262
- 0
lib/ios/HMSegmentedControl.h View File

@@ -0,0 +1,262 @@
1
+//
2
+//  HMSegmentedControl.h
3
+//  HMSegmentedControl
4
+//
5
+//  Created by Hesham Abd-Elmegid on 23/12/12.
6
+//  Copyright (c) 2012-2015 Hesham Abd-Elmegid. All rights reserved.
7
+//
8
+
9
+#import <UIKit/UIKit.h>
10
+
11
+@class HMSegmentedControl;
12
+
13
+typedef void (^IndexChangeBlock)(NSInteger index);
14
+typedef NSAttributedString *(^HMTitleFormatterBlock)(HMSegmentedControl *segmentedControl, NSString *title, NSUInteger index, BOOL selected);
15
+
16
+typedef NS_ENUM(NSInteger, HMSegmentedControlSelectionStyle) {
17
+    HMSegmentedControlSelectionStyleTextWidthStripe, // Indicator width will only be as big as the text width
18
+    HMSegmentedControlSelectionStyleFullWidthStripe, // Indicator width will fill the whole segment
19
+    HMSegmentedControlSelectionStyleBox, // A rectangle that covers the whole segment
20
+    HMSegmentedControlSelectionStyleArrow // An arrow in the middle of the segment pointing up or down depending on `HMSegmentedControlSelectionIndicatorLocation`
21
+};
22
+
23
+typedef NS_ENUM(NSInteger, HMSegmentedControlSelectionIndicatorLocation) {
24
+    HMSegmentedControlSelectionIndicatorLocationUp,
25
+    HMSegmentedControlSelectionIndicatorLocationDown,
26
+	HMSegmentedControlSelectionIndicatorLocationNone // No selection indicator
27
+};
28
+
29
+typedef NS_ENUM(NSInteger, HMSegmentedControlSegmentWidthStyle) {
30
+    HMSegmentedControlSegmentWidthStyleFixed, // Segment width is fixed
31
+    HMSegmentedControlSegmentWidthStyleDynamic, // Segment width will only be as big as the text width (including inset)
32
+};
33
+
34
+typedef NS_OPTIONS(NSInteger, HMSegmentedControlBorderType) {
35
+    HMSegmentedControlBorderTypeNone = 0,
36
+    HMSegmentedControlBorderTypeTop = (1 << 0),
37
+    HMSegmentedControlBorderTypeLeft = (1 << 1),
38
+    HMSegmentedControlBorderTypeBottom = (1 << 2),
39
+    HMSegmentedControlBorderTypeRight = (1 << 3)
40
+};
41
+
42
+enum {
43
+    HMSegmentedControlNoSegment = -1   // Segment index for no selected segment
44
+};
45
+
46
+typedef NS_ENUM(NSInteger, HMSegmentedControlType) {
47
+    HMSegmentedControlTypeText,
48
+    HMSegmentedControlTypeImages,
49
+	HMSegmentedControlTypeTextImages
50
+};
51
+
52
+typedef NS_ENUM(NSInteger, HMSegmentedControlImagePosition) {
53
+    HMSegmentedControlImagePositionBehindText,
54
+    HMSegmentedControlImagePositionLeftOfText,
55
+    HMSegmentedControlImagePositionRightOfText,
56
+    HMSegmentedControlImagePositionAboveText,
57
+    HMSegmentedControlImagePositionBelowText
58
+};
59
+
60
+@interface HMSegmentedControl : UIControl
61
+
62
+@property (nonatomic, strong) NSArray<NSString *> *sectionTitles;
63
+@property (nonatomic, strong) NSArray<UIImage *> *sectionImages;
64
+@property (nonatomic, strong) NSArray<UIImage *> *sectionSelectedImages;
65
+
66
+/**
67
+ Provide a block to be executed when selected index is changed.
68
+ 
69
+ Alternativly, you could use `addTarget:action:forControlEvents:`
70
+ */
71
+@property (nonatomic, copy) IndexChangeBlock indexChangeBlock;
72
+
73
+/**
74
+ Used to apply custom text styling to titles when set.
75
+ 
76
+ When this block is set, no additional styling is applied to the `NSAttributedString` object returned from this block.
77
+ */
78
+@property (nonatomic, copy) HMTitleFormatterBlock titleFormatter;
79
+
80
+/**
81
+ Text attributes to apply to item title text.
82
+ */
83
+@property (nonatomic, strong) NSDictionary *titleTextAttributes UI_APPEARANCE_SELECTOR;
84
+
85
+/*
86
+ Text attributes to apply to selected item title text.
87
+ 
88
+ Attributes not set in this dictionary are inherited from `titleTextAttributes`.
89
+ */
90
+@property (nonatomic, strong) NSDictionary *selectedTitleTextAttributes UI_APPEARANCE_SELECTOR;
91
+
92
+/**
93
+ Segmented control background color.
94
+ 
95
+ Default is `[UIColor whiteColor]`
96
+ */
97
+@property (nonatomic, strong) UIColor *backgroundColor UI_APPEARANCE_SELECTOR;
98
+
99
+/**
100
+ Color for the selection indicator stripe
101
+ 
102
+ Default is `R:52, G:181, B:229`
103
+ */
104
+@property (nonatomic, strong) UIColor *selectionIndicatorColor UI_APPEARANCE_SELECTOR;
105
+
106
+/**
107
+ Color for the selection indicator box
108
+ 
109
+ Default is selectionIndicatorColor
110
+ */
111
+@property (nonatomic, strong) UIColor *selectionIndicatorBoxColor UI_APPEARANCE_SELECTOR;
112
+
113
+/**
114
+ Color for the vertical divider between segments.
115
+ 
116
+ Default is `[UIColor blackColor]`
117
+ */
118
+@property (nonatomic, strong) UIColor *verticalDividerColor UI_APPEARANCE_SELECTOR;
119
+
120
+/**
121
+ Opacity for the seletion indicator box.
122
+ 
123
+ Default is `0.2f`
124
+ */
125
+@property (nonatomic) CGFloat selectionIndicatorBoxOpacity;
126
+
127
+/**
128
+ Width the vertical divider between segments that is added when `verticalDividerEnabled` is set to YES.
129
+ 
130
+ Default is `1.0f`
131
+ */
132
+@property (nonatomic, assign) CGFloat verticalDividerWidth;
133
+
134
+/**
135
+ Specifies the style of the control
136
+ 
137
+ Default is `HMSegmentedControlTypeText`
138
+ */
139
+@property (nonatomic, assign) HMSegmentedControlType type;
140
+
141
+/**
142
+ Specifies the style of the selection indicator.
143
+ 
144
+ Default is `HMSegmentedControlSelectionStyleTextWidthStripe`
145
+ */
146
+@property (nonatomic, assign) HMSegmentedControlSelectionStyle selectionStyle;
147
+
148
+/**
149
+ Specifies the style of the segment's width.
150
+ 
151
+ Default is `HMSegmentedControlSegmentWidthStyleFixed`
152
+ */
153
+@property (nonatomic, assign) HMSegmentedControlSegmentWidthStyle segmentWidthStyle;
154
+
155
+/**
156
+ Specifies the location of the selection indicator.
157
+ 
158
+ Default is `HMSegmentedControlSelectionIndicatorLocationUp`
159
+ */
160
+@property (nonatomic, assign) HMSegmentedControlSelectionIndicatorLocation selectionIndicatorLocation;
161
+
162
+/*
163
+ Specifies the border type.
164
+ 
165
+ Default is `HMSegmentedControlBorderTypeNone`
166
+ */
167
+@property (nonatomic, assign) HMSegmentedControlBorderType borderType;
168
+
169
+/**
170
+ Specifies the image position relative to the text. Only applicable for HMSegmentedControlTypeTextImages
171
+ 
172
+ Default is `HMSegmentedControlImagePositionBehindText`
173
+ */
174
+@property (nonatomic) HMSegmentedControlImagePosition imagePosition;
175
+
176
+/**
177
+ Specifies the distance between the text and the image. Only applicable for HMSegmentedControlTypeTextImages
178
+ 
179
+ Default is `0,0`
180
+ */
181
+@property (nonatomic) CGFloat textImageSpacing;
182
+
183
+/**
184
+ Specifies the border color.
185
+ 
186
+ Default is `[UIColor blackColor]`
187
+ */
188
+@property (nonatomic, strong) UIColor *borderColor;
189
+
190
+/**
191
+ Specifies the border width.
192
+ 
193
+ Default is `1.0f`
194
+ */
195
+@property (nonatomic, assign) CGFloat borderWidth;
196
+
197
+/**
198
+ Default is YES. Set to NO to deny scrolling by dragging the scrollView by the user.
199
+ */
200
+@property(nonatomic, getter = isUserDraggable) BOOL userDraggable;
201
+
202
+/**
203
+ Default is YES. Set to NO to deny any touch events by the user.
204
+ */
205
+@property(nonatomic, getter = isTouchEnabled) BOOL touchEnabled;
206
+
207
+/**
208
+ Default is NO. Set to YES to show a vertical divider between the segments.
209
+ */
210
+@property(nonatomic, getter = isVerticalDividerEnabled) BOOL verticalDividerEnabled;
211
+
212
+@property (nonatomic, getter=shouldStretchSegmentsToScreenSize) BOOL stretchSegmentsToScreenSize;
213
+
214
+/**
215
+ Index of the currently selected segment.
216
+ */
217
+@property (nonatomic, assign) NSInteger selectedSegmentIndex;
218
+
219
+/**
220
+ Height of the selection indicator. Only effective when `HMSegmentedControlSelectionStyle` is either `HMSegmentedControlSelectionStyleTextWidthStripe` or `HMSegmentedControlSelectionStyleFullWidthStripe`.
221
+ 
222
+ Default is 5.0
223
+ */
224
+@property (nonatomic, readwrite) CGFloat selectionIndicatorHeight;
225
+
226
+/**
227
+ Edge insets for the selection indicator.
228
+ NOTE: This does not affect the bounding box of HMSegmentedControlSelectionStyleBox
229
+ 
230
+ When HMSegmentedControlSelectionIndicatorLocationUp is selected, bottom edge insets are not used
231
+ 
232
+ When HMSegmentedControlSelectionIndicatorLocationDown is selected, top edge insets are not used
233
+ 
234
+ Defaults are top: 0.0f
235
+             left: 0.0f
236
+           bottom: 0.0f
237
+            right: 0.0f
238
+ */
239
+@property (nonatomic, readwrite) UIEdgeInsets selectionIndicatorEdgeInsets;
240
+
241
+/**
242
+ Inset left and right edges of segments.
243
+ 
244
+ Default is UIEdgeInsetsMake(0, 5, 0, 5)
245
+ */
246
+@property (nonatomic, readwrite) UIEdgeInsets segmentEdgeInset;
247
+
248
+@property (nonatomic, readwrite) UIEdgeInsets enlargeEdgeInset;
249
+
250
+/**
251
+ Default is YES. Set to NO to disable animation during user selection.
252
+ */
253
+@property (nonatomic) BOOL shouldAnimateUserSelection;
254
+
255
+- (id)initWithSectionTitles:(NSArray<NSString *> *)sectiontitles;
256
+- (id)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages;
257
+- (instancetype)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages titlesForSections:(NSArray<NSString *> *)sectiontitles;
258
+- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated;
259
+- (void)setIndexChangeBlock:(IndexChangeBlock)indexChangeBlock;
260
+- (void)setTitleFormatter:(HMTitleFormatterBlock)titleFormatter;
261
+
262
+@end

+ 958
- 0
lib/ios/HMSegmentedControl.m View File

@@ -0,0 +1,958 @@
1
+//
2
+//  HMSegmentedControl.m
3
+//  HMSegmentedControl
4
+//
5
+//  Created by Hesham Abd-Elmegid on 23/12/12.
6
+//  Copyright (c) 2012-2015 Hesham Abd-Elmegid. All rights reserved.
7
+//
8
+
9
+#import "HMSegmentedControl.h"
10
+#import <QuartzCore/QuartzCore.h>
11
+#import <math.h>
12
+
13
+@interface HMScrollView : UIScrollView
14
+@end
15
+
16
+@interface HMSegmentedControl ()
17
+
18
+@property (nonatomic, strong) CALayer *selectionIndicatorStripLayer;
19
+@property (nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
20
+@property (nonatomic, strong) CALayer *selectionIndicatorArrowLayer;
21
+@property (nonatomic, readwrite) CGFloat segmentWidth;
22
+@property (nonatomic, readwrite) NSArray<NSNumber *> *segmentWidthsArray;
23
+@property (nonatomic, strong) HMScrollView *scrollView;
24
+
25
+@end
26
+
27
+@implementation HMScrollView
28
+
29
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
30
+    if (!self.dragging) {
31
+        [self.nextResponder touchesBegan:touches withEvent:event];
32
+    } else {
33
+        [super touchesBegan:touches withEvent:event];
34
+    }
35
+}
36
+
37
+- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
38
+    if (!self.dragging) {
39
+        [self.nextResponder touchesMoved:touches withEvent:event];
40
+    } else{
41
+        [super touchesMoved:touches withEvent:event];
42
+    }
43
+}
44
+
45
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
46
+    if (!self.dragging) {
47
+        [self.nextResponder touchesEnded:touches withEvent:event];
48
+    } else {
49
+        [super touchesEnded:touches withEvent:event];
50
+    }
51
+}
52
+
53
+@end
54
+
55
+@implementation HMSegmentedControl
56
+
57
+- (id)initWithCoder:(NSCoder *)aDecoder {
58
+    self = [super initWithCoder:aDecoder];
59
+    if (self) {
60
+        [self commonInit];
61
+    }
62
+    return self;
63
+}
64
+
65
+- (id)initWithFrame:(CGRect)frame {
66
+    self = [super initWithFrame:frame];
67
+    
68
+    if (self) {
69
+        [self commonInit];
70
+    }
71
+    
72
+    return self;
73
+}
74
+
75
+- (id)initWithSectionTitles:(NSArray<NSString *> *)sectiontitles {
76
+    self = [super initWithFrame:CGRectZero];
77
+    
78
+    if (self) {
79
+        [self commonInit];
80
+        self.sectionTitles = sectiontitles;
81
+        self.type = HMSegmentedControlTypeText;
82
+    }
83
+    
84
+    return self;
85
+}
86
+
87
+- (id)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages {
88
+    self = [super initWithFrame:CGRectZero];
89
+    
90
+    if (self) {
91
+        [self commonInit];
92
+        self.sectionImages = sectionImages;
93
+        self.sectionSelectedImages = sectionSelectedImages;
94
+        self.type = HMSegmentedControlTypeImages;
95
+    }
96
+    
97
+    return self;
98
+}
99
+
100
+- (instancetype)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages titlesForSections:(NSArray<NSString *> *)sectiontitles {
101
+	self = [super initWithFrame:CGRectZero];
102
+    
103
+    if (self) {
104
+        [self commonInit];
105
+		
106
+		if (sectionImages.count != sectiontitles.count) {
107
+			[NSException raise:NSRangeException format:@"***%s: Images bounds (%ld) Don't match Title bounds (%ld)", sel_getName(_cmd), (unsigned long)sectionImages.count, (unsigned long)sectiontitles.count];
108
+        }
109
+		
110
+        self.sectionImages = sectionImages;
111
+        self.sectionSelectedImages = sectionSelectedImages;
112
+		self.sectionTitles = sectiontitles;
113
+        self.type = HMSegmentedControlTypeTextImages;
114
+    }
115
+    
116
+    return self;
117
+}
118
+
119
+- (void)awakeFromNib {
120
+    [super awakeFromNib];
121
+    
122
+    self.segmentWidth = 0.0f;
123
+}
124
+
125
+- (void)commonInit {
126
+    self.scrollView = [[HMScrollView alloc] init];
127
+    self.scrollView.scrollsToTop = NO;
128
+    self.scrollView.showsVerticalScrollIndicator = NO;
129
+    self.scrollView.showsHorizontalScrollIndicator = NO;
130
+    [self addSubview:self.scrollView];
131
+    
132
+    _backgroundColor = [UIColor whiteColor];
133
+    self.opaque = NO;
134
+    _selectionIndicatorColor = [UIColor colorWithRed:52.0f/255.0f green:181.0f/255.0f blue:229.0f/255.0f alpha:1.0f];
135
+    _selectionIndicatorBoxColor = _selectionIndicatorColor;
136
+
137
+    self.selectedSegmentIndex = 0;
138
+    self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
139
+    self.selectionIndicatorHeight = 5.0f;
140
+    self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
141
+    self.selectionStyle = HMSegmentedControlSelectionStyleTextWidthStripe;
142
+    self.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationUp;
143
+    self.segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
144
+    self.userDraggable = YES;
145
+    self.touchEnabled = YES;
146
+    self.verticalDividerEnabled = NO;
147
+    self.type = HMSegmentedControlTypeText;
148
+    self.verticalDividerWidth = 1.0f;
149
+    _verticalDividerColor = [UIColor blackColor];
150
+    self.borderColor = [UIColor blackColor];
151
+    self.borderWidth = 1.0f;
152
+    
153
+    self.shouldAnimateUserSelection = YES;
154
+    
155
+    self.selectionIndicatorArrowLayer = [CALayer layer];
156
+    self.selectionIndicatorStripLayer = [CALayer layer];
157
+    self.selectionIndicatorBoxLayer = [CALayer layer];
158
+    self.selectionIndicatorBoxLayer.opacity = self.selectionIndicatorBoxOpacity;
159
+    self.selectionIndicatorBoxLayer.borderWidth = 1.0f;
160
+    self.selectionIndicatorBoxOpacity = 0.2;
161
+    
162
+    self.contentMode = UIViewContentModeRedraw;
163
+}
164
+
165
+- (void)layoutSubviews {
166
+    [super layoutSubviews];
167
+    
168
+    [self updateSegmentsRects];
169
+}
170
+
171
+- (void)setFrame:(CGRect)frame {
172
+    [super setFrame:frame];
173
+    
174
+    [self updateSegmentsRects];
175
+}
176
+
177
+- (void)setSectionTitles:(NSArray<NSString *> *)sectionTitles {
178
+    _sectionTitles = sectionTitles;
179
+    
180
+    [self setNeedsLayout];
181
+    [self setNeedsDisplay];
182
+}
183
+
184
+- (void)setSectionImages:(NSArray<UIImage *> *)sectionImages {
185
+    _sectionImages = sectionImages;
186
+    
187
+    [self setNeedsLayout];
188
+    [self setNeedsDisplay];
189
+}
190
+
191
+- (void)setSelectionIndicatorLocation:(HMSegmentedControlSelectionIndicatorLocation)selectionIndicatorLocation {
192
+	_selectionIndicatorLocation = selectionIndicatorLocation;
193
+	
194
+	if (selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationNone) {
195
+		self.selectionIndicatorHeight = 0.0f;
196
+	}
197
+}
198
+
199
+- (void)setSelectionIndicatorBoxOpacity:(CGFloat)selectionIndicatorBoxOpacity {
200
+    _selectionIndicatorBoxOpacity = selectionIndicatorBoxOpacity;
201
+    
202
+    self.selectionIndicatorBoxLayer.opacity = _selectionIndicatorBoxOpacity;
203
+}
204
+
205
+- (void)setSegmentWidthStyle:(HMSegmentedControlSegmentWidthStyle)segmentWidthStyle {
206
+    // Force HMSegmentedControlSegmentWidthStyleFixed when type is HMSegmentedControlTypeImages.
207
+    if (self.type == HMSegmentedControlTypeImages) {
208
+        _segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
209
+    } else {
210
+        _segmentWidthStyle = segmentWidthStyle;
211
+    }
212
+}
213
+
214
+- (void)setBorderType:(HMSegmentedControlBorderType)borderType {
215
+    _borderType = borderType;
216
+    [self setNeedsDisplay];
217
+}
218
+
219
+#pragma mark - Drawing
220
+
221
+- (CGSize)measureTitleAtIndex:(NSUInteger)index {
222
+    if (index >= self.sectionTitles.count) {
223
+        return CGSizeZero;
224
+    }
225
+    
226
+    id title = self.sectionTitles[index];
227
+    CGSize size = CGSizeZero;
228
+    BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
229
+    if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {
230
+        NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
231
+        size = [(NSString *)title sizeWithAttributes:titleAttrs];
232
+    } else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {
233
+        size = [self.titleFormatter(self, title, index, selected) size];
234
+    } else if ([title isKindOfClass:[NSAttributedString class]]) {
235
+        size = [(NSAttributedString *)title size];
236
+    } else {
237
+        NSAssert(title == nil, @"Unexpected type of segment title: %@", [title class]);
238
+        size = CGSizeZero;
239
+    }
240
+    return CGRectIntegral((CGRect){CGPointZero, size}).size;
241
+}
242
+
243
+- (NSAttributedString *)attributedTitleAtIndex:(NSUInteger)index {
244
+    id title = self.sectionTitles[index];
245
+    BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
246
+    
247
+    if ([title isKindOfClass:[NSAttributedString class]]) {
248
+        return (NSAttributedString *)title;
249
+    } else if (!self.titleFormatter) {
250
+        NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
251
+        
252
+        // the color should be cast to CGColor in order to avoid invalid context on iOS7
253
+        UIColor *titleColor = titleAttrs[NSForegroundColorAttributeName];
254
+        
255
+        if (titleColor) {
256
+            NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:titleAttrs];
257
+            
258
+            dict[NSForegroundColorAttributeName] = (id)titleColor.CGColor;
259
+            
260
+            titleAttrs = [NSDictionary dictionaryWithDictionary:dict];
261
+        }
262
+        
263
+        return [[NSAttributedString alloc] initWithString:(NSString *)title attributes:titleAttrs];
264
+    } else {
265
+        return self.titleFormatter(self, title, index, selected);
266
+    }
267
+}
268
+
269
+- (void)drawRect:(CGRect)rect {
270
+    [self.backgroundColor setFill];
271
+    UIRectFill([self bounds]);
272
+    
273
+    self.selectionIndicatorArrowLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
274
+    
275
+    self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
276
+    
277
+    self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorBoxColor.CGColor;
278
+    self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorBoxColor.CGColor;
279
+    
280
+    // Remove all sublayers to avoid drawing images over existing ones
281
+    self.scrollView.layer.sublayers = nil;
282
+    
283
+    CGRect oldRect = rect;
284
+    
285
+    if (self.type == HMSegmentedControlTypeText) {
286
+        [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
287
+
288
+            CGFloat stringWidth = 0;
289
+            CGFloat stringHeight = 0;
290
+            CGSize size = [self measureTitleAtIndex:idx];
291
+            stringWidth = size.width;
292
+            stringHeight = size.height;
293
+            CGRect rectDiv = CGRectZero;
294
+            CGRect fullRect = CGRectZero;
295
+            
296
+            // Text inside the CATextLayer will appear blurry unless the rect values are rounded
297
+            BOOL locationUp = (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp);
298
+            BOOL selectionStyleNotBox = (self.selectionStyle != HMSegmentedControlSelectionStyleBox);
299
+
300
+            CGFloat y = roundf((CGRectGetHeight(self.frame) - selectionStyleNotBox * self.selectionIndicatorHeight) / 2 - stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
301
+            CGRect rect;
302
+            if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
303
+                rect = CGRectMake((self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2, y, stringWidth, stringHeight);
304
+                rectDiv = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
305
+                fullRect = CGRectMake(self.segmentWidth * idx, 0, self.segmentWidth, oldRect.size.height);
306
+            } else {
307
+                // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
308
+                CGFloat xOffset = 0;
309
+                NSInteger i = 0;
310
+                for (NSNumber *width in self.segmentWidthsArray) {
311
+                    if (idx == i)
312
+                        break;
313
+                    xOffset = xOffset + [width floatValue];
314
+                    i++;
315
+                }
316
+                
317
+                CGFloat widthForIndex = [[self.segmentWidthsArray objectAtIndex:idx] floatValue];
318
+                rect = CGRectMake(xOffset, y, widthForIndex, stringHeight);
319
+                fullRect = CGRectMake(self.segmentWidth * idx, 0, widthForIndex, oldRect.size.height);
320
+                rectDiv = CGRectMake(xOffset - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
321
+            }
322
+            
323
+            // Fix rect position/size to avoid blurry labels
324
+            rect = CGRectMake(ceilf(rect.origin.x), ceilf(rect.origin.y), ceilf(rect.size.width), ceilf(rect.size.height));
325
+            
326
+            CATextLayer *titleLayer = [CATextLayer layer];
327
+            titleLayer.frame = rect;
328
+            titleLayer.alignmentMode = kCAAlignmentCenter;
329
+            if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
330
+                titleLayer.truncationMode = kCATruncationEnd;
331
+            }
332
+            titleLayer.string = [self attributedTitleAtIndex:idx];
333
+            titleLayer.contentsScale = [[UIScreen mainScreen] scale];
334
+            
335
+            [self.scrollView.layer addSublayer:titleLayer];
336
+            
337
+            // Vertical Divider
338
+            if (self.isVerticalDividerEnabled && idx > 0) {
339
+                CALayer *verticalDividerLayer = [CALayer layer];
340
+                verticalDividerLayer.frame = rectDiv;
341
+                verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
342
+                
343
+                [self.scrollView.layer addSublayer:verticalDividerLayer];
344
+            }
345
+        
346
+            [self addBackgroundAndBorderLayerWithRect:fullRect];
347
+        }];
348
+    } else if (self.type == HMSegmentedControlTypeImages) {
349
+        [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
350
+            UIImage *icon = iconImage;
351
+            CGFloat imageWidth = icon.size.width;
352
+            CGFloat imageHeight = icon.size.height;
353
+            CGFloat y = roundf(CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2 - imageHeight / 2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0);
354
+            CGFloat x = self.segmentWidth * idx + (self.segmentWidth - imageWidth)/2.0f;
355
+            CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
356
+            
357
+            CALayer *imageLayer = [CALayer layer];
358
+            imageLayer.frame = rect;
359
+            
360
+            if (self.selectedSegmentIndex == idx) {
361
+                if (self.sectionSelectedImages) {
362
+                    UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
363
+                    imageLayer.contents = (id)highlightIcon.CGImage;
364
+                } else {
365
+                    imageLayer.contents = (id)icon.CGImage;
366
+                }
367
+            } else {
368
+                imageLayer.contents = (id)icon.CGImage;
369
+            }
370
+            
371
+            [self.scrollView.layer addSublayer:imageLayer];
372
+            // Vertical Divider
373
+            if (self.isVerticalDividerEnabled && idx>0) {
374
+                CALayer *verticalDividerLayer = [CALayer layer];
375
+                verticalDividerLayer.frame = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height-(self.selectionIndicatorHeight * 4));
376
+                verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
377
+                
378
+                [self.scrollView.layer addSublayer:verticalDividerLayer];
379
+            }
380
+            
381
+            [self addBackgroundAndBorderLayerWithRect:rect];
382
+        }];
383
+    } else if (self.type == HMSegmentedControlTypeTextImages){
384
+		[self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
385
+            UIImage *icon = iconImage;
386
+            CGFloat imageWidth = icon.size.width;
387
+            CGFloat imageHeight = icon.size.height;
388
+			
389
+            CGSize stringSize = [self measureTitleAtIndex:idx];
390
+            CGFloat stringHeight = stringSize.height;
391
+            CGFloat stringWidth = stringSize.width;
392
+            
393
+            CGFloat imageXOffset = self.segmentWidth * idx; // Start with edge inset
394
+            CGFloat textXOffset  = self.segmentWidth * idx;
395
+            CGFloat imageYOffset = ceilf((self.frame.size.height - imageHeight) / 2.0); // Start in center
396
+            CGFloat textYOffset  = ceilf((self.frame.size.height - stringHeight) / 2.0);
397
+            
398
+            
399
+            if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
400
+                BOOL isImageInLineWidthText = self.imagePosition == HMSegmentedControlImagePositionLeftOfText || self.imagePosition == HMSegmentedControlImagePositionRightOfText;
401
+                if (isImageInLineWidthText) {
402
+                    CGFloat whitespace = self.segmentWidth - stringSize.width - imageWidth - self.textImageSpacing;
403
+                    if (self.imagePosition == HMSegmentedControlImagePositionLeftOfText) {
404
+                        imageXOffset += whitespace / 2.0;
405
+                        textXOffset = imageXOffset + imageWidth + self.textImageSpacing;
406
+                    } else {
407
+                        textXOffset += whitespace / 2.0;
408
+                        imageXOffset = textXOffset + stringWidth + self.textImageSpacing;
409
+                    }
410
+                } else {
411
+                    imageXOffset = self.segmentWidth * idx + (self.segmentWidth - imageWidth) / 2.0f; // Start with edge inset
412
+                    textXOffset  = self.segmentWidth * idx + (self.segmentWidth - stringWidth) / 2.0f;
413
+                    
414
+                    CGFloat whitespace = CGRectGetHeight(self.frame) - imageHeight - stringHeight - self.textImageSpacing;
415
+                    if (self.imagePosition == HMSegmentedControlImagePositionAboveText) {
416
+                        imageYOffset = ceilf(whitespace / 2.0);
417
+                        textYOffset = imageYOffset + imageHeight + self.textImageSpacing;
418
+                    } else if (self.imagePosition == HMSegmentedControlImagePositionBelowText) {
419
+                        textYOffset = ceilf(whitespace / 2.0);
420
+                        imageYOffset = textYOffset + stringHeight + self.textImageSpacing;
421
+                    }
422
+                }
423
+            } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
424
+                // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
425
+                CGFloat xOffset = 0;
426
+                NSInteger i = 0;
427
+                
428
+                for (NSNumber *width in self.segmentWidthsArray) {
429
+                    if (idx == i) {
430
+                        break;
431
+                    }
432
+                    
433
+                    xOffset = xOffset + [width floatValue];
434
+                    i++;
435
+                }
436
+                
437
+                BOOL isImageInLineWidthText = self.imagePosition == HMSegmentedControlImagePositionLeftOfText || self.imagePosition == HMSegmentedControlImagePositionRightOfText;
438
+                if (isImageInLineWidthText) {
439
+                    if (self.imagePosition == HMSegmentedControlImagePositionLeftOfText) {
440
+                        imageXOffset = xOffset;
441
+                        textXOffset = imageXOffset + imageWidth + self.textImageSpacing;
442
+                    } else {
443
+                        textXOffset = xOffset;
444
+                        imageXOffset = textXOffset + stringWidth + self.textImageSpacing;
445
+                    }
446
+                } else {
447
+                    imageXOffset = xOffset + ([self.segmentWidthsArray[i] floatValue] - imageWidth) / 2.0f; // Start with edge inset
448
+                    textXOffset  = xOffset + ([self.segmentWidthsArray[i] floatValue] - stringWidth) / 2.0f;
449
+                    
450
+                    CGFloat whitespace = CGRectGetHeight(self.frame) - imageHeight - stringHeight - self.textImageSpacing;
451
+                    if (self.imagePosition == HMSegmentedControlImagePositionAboveText) {
452
+                        imageYOffset = ceilf(whitespace / 2.0);
453
+                        textYOffset = imageYOffset + imageHeight + self.textImageSpacing;
454
+                    } else if (self.imagePosition == HMSegmentedControlImagePositionBelowText) {
455
+                        textYOffset = ceilf(whitespace / 2.0);
456
+                        imageYOffset = textYOffset + stringHeight + self.textImageSpacing;
457
+                    }
458
+                }
459
+            }
460
+            
461
+            CGRect imageRect = CGRectMake(imageXOffset, imageYOffset, imageWidth, imageHeight);
462
+            CGRect textRect = CGRectMake(ceilf(textXOffset), ceilf(textYOffset), ceilf(stringWidth), ceilf(stringHeight));
463
+
464
+            CATextLayer *titleLayer = [CATextLayer layer];
465
+            titleLayer.frame = textRect;
466
+            titleLayer.alignmentMode = kCAAlignmentCenter;
467
+            titleLayer.string = [self attributedTitleAtIndex:idx];
468
+            if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
469
+                titleLayer.truncationMode = kCATruncationEnd;
470
+            }
471
+            CALayer *imageLayer = [CALayer layer];
472
+            imageLayer.frame = imageRect;
473
+			
474
+            if (self.selectedSegmentIndex == idx) {
475
+                if (self.sectionSelectedImages) {
476
+                    UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
477
+                    imageLayer.contents = (id)highlightIcon.CGImage;
478
+                } else {
479
+                    imageLayer.contents = (id)icon.CGImage;
480
+                }
481
+            } else {
482
+                imageLayer.contents = (id)icon.CGImage;
483
+            }
484
+            
485
+            [self.scrollView.layer addSublayer:imageLayer];
486
+			titleLayer.contentsScale = [[UIScreen mainScreen] scale];
487
+            [self.scrollView.layer addSublayer:titleLayer];
488
+			
489
+            [self addBackgroundAndBorderLayerWithRect:imageRect];
490
+        }];
491
+	}
492
+    
493
+    // Add the selection indicators
494
+    if (self.selectedSegmentIndex != HMSegmentedControlNoSegment) {
495
+        if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
496
+            if (!self.selectionIndicatorArrowLayer.superlayer) {
497
+                [self setArrowFrame];
498
+                [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
499
+            }
500
+        } else {
501
+            if (!self.selectionIndicatorStripLayer.superlayer) {
502
+                self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
503
+                [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
504
+                
505
+                if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && !self.selectionIndicatorBoxLayer.superlayer) {
506
+                    self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
507
+                    [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
508
+                }
509
+            }
510
+        }
511
+    }
512
+}
513
+
514
+- (void)addBackgroundAndBorderLayerWithRect:(CGRect)fullRect {
515
+    // Background layer
516
+    CALayer *backgroundLayer = [CALayer layer];
517
+    backgroundLayer.frame = fullRect;
518
+    [self.layer insertSublayer:backgroundLayer atIndex:0];
519
+    
520
+    // Border layer
521
+    if (self.borderType & HMSegmentedControlBorderTypeTop) {
522
+        CALayer *borderLayer = [CALayer layer];
523
+        borderLayer.frame = CGRectMake(0, 0, fullRect.size.width, self.borderWidth);
524
+        borderLayer.backgroundColor = self.borderColor.CGColor;
525
+        [backgroundLayer addSublayer: borderLayer];
526
+    }
527
+    if (self.borderType & HMSegmentedControlBorderTypeLeft) {
528
+        CALayer *borderLayer = [CALayer layer];
529
+        borderLayer.frame = CGRectMake(0, 0, self.borderWidth, fullRect.size.height);
530
+        borderLayer.backgroundColor = self.borderColor.CGColor;
531
+        [backgroundLayer addSublayer: borderLayer];
532
+    }
533
+    if (self.borderType & HMSegmentedControlBorderTypeBottom) {
534
+        CALayer *borderLayer = [CALayer layer];
535
+        borderLayer.frame = CGRectMake(0, fullRect.size.height - self.borderWidth, fullRect.size.width, self.borderWidth);
536
+        borderLayer.backgroundColor = self.borderColor.CGColor;
537
+        [backgroundLayer addSublayer: borderLayer];
538
+    }
539
+    if (self.borderType & HMSegmentedControlBorderTypeRight) {
540
+        CALayer *borderLayer = [CALayer layer];
541
+        borderLayer.frame = CGRectMake(fullRect.size.width - self.borderWidth, 0, self.borderWidth, fullRect.size.height);
542
+        borderLayer.backgroundColor = self.borderColor.CGColor;
543
+        [backgroundLayer addSublayer: borderLayer];
544
+    }
545
+}
546
+
547
+- (void)setArrowFrame {
548
+    self.selectionIndicatorArrowLayer.frame = [self frameForSelectionIndicator];
549
+    
550
+    self.selectionIndicatorArrowLayer.mask = nil;
551
+    
552
+    UIBezierPath *arrowPath = [UIBezierPath bezierPath];
553
+    
554
+    CGPoint p1 = CGPointZero;
555
+    CGPoint p2 = CGPointZero;
556
+    CGPoint p3 = CGPointZero;
557
+    
558
+    if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
559
+        p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, 0);
560
+        p2 = CGPointMake(0, self.selectionIndicatorArrowLayer.bounds.size.height);
561
+        p3 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, self.selectionIndicatorArrowLayer.bounds.size.height);
562
+    }
563
+    
564
+    if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
565
+        p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, self.selectionIndicatorArrowLayer.bounds.size.height);
566
+        p2 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, 0);
567
+        p3 = CGPointMake(0, 0);
568
+    }
569
+    
570
+    [arrowPath moveToPoint:p1];
571
+    [arrowPath addLineToPoint:p2];
572
+    [arrowPath addLineToPoint:p3];
573
+    [arrowPath closePath];
574
+    
575
+    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
576
+    maskLayer.frame = self.selectionIndicatorArrowLayer.bounds;
577
+    maskLayer.path = arrowPath.CGPath;
578
+    self.selectionIndicatorArrowLayer.mask = maskLayer;
579
+}
580
+
581
+- (CGRect)frameForSelectionIndicator {
582
+    CGFloat indicatorYOffset = 0.0f;
583
+    
584
+    if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
585
+        indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom;
586
+    }
587
+    
588
+    if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
589
+        indicatorYOffset = self.selectionIndicatorEdgeInsets.top;
590
+    }
591
+    
592
+    CGFloat sectionWidth = 0.0f;
593
+    
594
+    if (self.type == HMSegmentedControlTypeText) {
595
+        CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
596
+        sectionWidth = stringWidth;
597
+    } else if (self.type == HMSegmentedControlTypeImages) {
598
+        UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
599
+        CGFloat imageWidth = sectionImage.size.width;
600
+        sectionWidth = imageWidth;
601
+    } else if (self.type == HMSegmentedControlTypeTextImages) {
602
+		CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
603
+		UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
604
+		CGFloat imageWidth = sectionImage.size.width;
605
+        sectionWidth = MAX(stringWidth, imageWidth);
606
+	}
607
+    
608
+    if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
609
+        CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
610
+        CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
611
+        
612
+        CGFloat x = widthToStartOfSelectedIndex + ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) - (self.selectionIndicatorHeight/2);
613
+        return CGRectMake(x - (self.selectionIndicatorHeight / 2), indicatorYOffset, self.selectionIndicatorHeight * 2, self.selectionIndicatorHeight);
614
+    } else {
615
+        if (self.selectionStyle == HMSegmentedControlSelectionStyleTextWidthStripe &&
616
+            sectionWidth <= self.segmentWidth &&
617
+            self.segmentWidthStyle != HMSegmentedControlSegmentWidthStyleDynamic) {
618
+            CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
619
+            CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
620
+            
621
+            CGFloat x = ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) + (widthToStartOfSelectedIndex - sectionWidth / 2);
622
+            return CGRectMake(x + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, sectionWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
623
+        } else {
624
+            if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
625
+                CGFloat selectedSegmentOffset = 0.0f;
626
+                
627
+                NSInteger i = 0;
628
+                for (NSNumber *width in self.segmentWidthsArray) {
629
+                    if (self.selectedSegmentIndex == i)
630
+                        break;
631
+                    selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
632
+                    i++;
633
+                }
634
+                return CGRectMake(selectedSegmentOffset + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom);
635
+            }
636
+            
637
+            return CGRectMake((self.segmentWidth + self.selectionIndicatorEdgeInsets.left) * self.selectedSegmentIndex, indicatorYOffset, self.segmentWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
638
+        }
639
+    }
640
+}
641
+
642
+- (CGRect)frameForFillerSelectionIndicator {
643
+    if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
644
+        CGFloat selectedSegmentOffset = 0.0f;
645
+        
646
+        NSInteger i = 0;
647
+        for (NSNumber *width in self.segmentWidthsArray) {
648
+            if (self.selectedSegmentIndex == i) {
649
+                break;
650
+            }
651
+            selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
652
+            
653
+            i++;
654
+        }
655
+        
656
+        return CGRectMake(selectedSegmentOffset, 0, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue], CGRectGetHeight(self.frame));
657
+    }
658
+    return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, 0, self.segmentWidth, CGRectGetHeight(self.frame));
659
+}
660
+
661
+- (void)updateSegmentsRects {
662
+    self.scrollView.contentInset = UIEdgeInsetsZero;
663
+    self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
664
+    
665
+    if ([self sectionCount] > 0) {
666
+        self.segmentWidth = self.frame.size.width / [self sectionCount];
667
+    }
668
+    
669
+    if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
670
+        [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
671
+            CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
672
+            self.segmentWidth = MAX(stringWidth, self.segmentWidth);
673
+        }];
674
+    } else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
675
+        NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
676
+        
677
+        [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
678
+            CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
679
+            [mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
680
+        }];
681
+        self.segmentWidthsArray = [mutableSegmentWidths copy];
682
+    } else if (self.type == HMSegmentedControlTypeImages) {
683
+        for (UIImage *sectionImage in self.sectionImages) {
684
+            CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
685
+            self.segmentWidth = MAX(imageWidth, self.segmentWidth);
686
+        }
687
+    } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed){
688
+        //lets just use the title.. we will assume it is wider then images...
689
+        [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
690
+            CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
691
+            self.segmentWidth = MAX(stringWidth, self.segmentWidth);
692
+        }];
693
+    } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
694
+        NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
695
+        __block CGFloat totalWidth = 0.0;
696
+        
697
+        int i = 0;
698
+        [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
699
+            CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.right;
700
+            UIImage *sectionImage = [self.sectionImages objectAtIndex:i];
701
+            CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left;
702
+            
703
+            CGFloat combinedWidth = 0.0;
704
+            if (self.imagePosition == HMSegmentedControlImagePositionLeftOfText || self.imagePosition == HMSegmentedControlImagePositionRightOfText) {
705
+                combinedWidth = imageWidth + stringWidth + self.textImageSpacing;
706
+            } else {
707
+                combinedWidth = MAX(imageWidth, stringWidth);
708
+            }
709
+            
710
+            totalWidth += combinedWidth;
711
+            
712
+            [mutableSegmentWidths addObject:[NSNumber numberWithFloat:combinedWidth]];
713
+        }];
714
+        
715
+        if (self.shouldStretchSegmentsToScreenSize && totalWidth < self.bounds.size.width) {
716
+            CGFloat whitespace = self.bounds.size.width - totalWidth;
717
+            CGFloat whitespaceForSegment = whitespace / [mutableSegmentWidths count];
718
+            [mutableSegmentWidths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
719
+                CGFloat extendedWidth = whitespaceForSegment + [obj floatValue];
720
+                [mutableSegmentWidths replaceObjectAtIndex:idx withObject:[NSNumber numberWithFloat:extendedWidth]];
721
+            }];
722
+        }
723
+        
724
+        self.segmentWidthsArray = [mutableSegmentWidths copy];
725
+    }
726
+
727
+    self.scrollView.scrollEnabled = self.isUserDraggable;
728
+    self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
729
+}
730
+
731
+- (NSUInteger)sectionCount {
732
+    if (self.type == HMSegmentedControlTypeText) {
733
+        return self.sectionTitles.count;
734
+    } else if (self.type == HMSegmentedControlTypeImages ||
735
+               self.type == HMSegmentedControlTypeTextImages) {
736
+        return self.sectionImages.count;
737
+    }
738
+    
739
+    return 0;
740
+}
741
+
742
+- (void)willMoveToSuperview:(UIView *)newSuperview {
743
+    // Control is being removed
744
+    if (newSuperview == nil)
745
+        return;
746
+    
747
+    if (self.sectionTitles || self.sectionImages) {
748
+        [self updateSegmentsRects];
749
+    }
750
+}
751
+
752
+#pragma mark - Touch
753
+
754
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
755
+    UITouch *touch = [touches anyObject];
756
+    CGPoint touchLocation = [touch locationInView:self];
757
+    
758
+    CGRect enlargeRect =   CGRectMake(self.bounds.origin.x - self.enlargeEdgeInset.left,
759
+                      self.bounds.origin.y - self.enlargeEdgeInset.top,
760
+                      self.bounds.size.width + self.enlargeEdgeInset.left + self.enlargeEdgeInset.right,
761
+                      self.bounds.size.height + self.enlargeEdgeInset.top + self.enlargeEdgeInset.bottom);
762
+    
763
+    if (CGRectContainsPoint(enlargeRect, touchLocation)) {
764
+        NSInteger segment = 0;
765
+        if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
766
+            segment = (touchLocation.x + self.scrollView.contentOffset.x) / self.segmentWidth;
767
+        } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
768
+            // To know which segment the user touched, we need to loop over the widths and substract it from the x position.
769
+            CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
770
+            for (NSNumber *width in self.segmentWidthsArray) {
771
+                widthLeft = widthLeft - [width floatValue];
772
+                
773
+                // When we don't have any width left to substract, we have the segment index.
774
+                if (widthLeft <= 0)
775
+                    break;
776
+                
777
+                segment++;
778
+            }
779
+        }
780
+        
781
+        NSUInteger sectionsCount = 0;
782
+        
783
+        if (self.type == HMSegmentedControlTypeImages) {
784
+            sectionsCount = [self.sectionImages count];
785
+        } else if (self.type == HMSegmentedControlTypeTextImages || self.type == HMSegmentedControlTypeText) {
786
+            sectionsCount = [self.sectionTitles count];
787
+        }
788
+        
789
+        if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
790
+            // Check if we have to do anything with the touch event
791
+            if (self.isTouchEnabled)
792
+                [self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
793
+        }
794
+    }
795
+}
796
+
797
+#pragma mark - Scrolling
798
+
799
+- (CGFloat)totalSegmentedControlWidth {
800
+    if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
801
+        return self.sectionTitles.count * self.segmentWidth;
802
+    } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
803
+        return [[self.segmentWidthsArray valueForKeyPath:@"@sum.self"] floatValue];
804
+    } else {
805
+        return self.sectionImages.count * self.segmentWidth;
806
+    }
807
+}
808
+
809
+- (void)scrollToSelectedSegmentIndex:(BOOL)animated {
810
+    CGRect rectForSelectedIndex = CGRectZero;
811
+    CGFloat selectedSegmentOffset = 0;
812
+    if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
813
+        rectForSelectedIndex = CGRectMake(self.segmentWidth * self.selectedSegmentIndex,
814
+                                          0,
815
+                                          self.segmentWidth,
816
+                                          self.frame.size.height);
817
+        
818
+        selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - (self.segmentWidth / 2);
819
+    } else {
820
+        NSInteger i = 0;
821
+        CGFloat offsetter = 0;
822
+        for (NSNumber *width in self.segmentWidthsArray) {
823
+            if (self.selectedSegmentIndex == i)
824
+                break;
825
+            offsetter = offsetter + [width floatValue];
826
+            i++;
827
+        }
828
+        
829
+        rectForSelectedIndex = CGRectMake(offsetter,
830
+                                          0,
831
+                                          [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue],
832
+                                          self.frame.size.height);
833
+        
834
+        selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - ([[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] / 2);
835
+    }
836
+    
837
+    
838
+    CGRect rectToScrollTo = rectForSelectedIndex;
839
+    rectToScrollTo.origin.x -= selectedSegmentOffset;
840
+    rectToScrollTo.size.width += selectedSegmentOffset * 2;
841
+    [self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];
842
+}
843
+
844
+#pragma mark - Index Change
845
+
846
+- (void)setSelectedSegmentIndex:(NSInteger)index {
847
+    [self setSelectedSegmentIndex:index animated:NO notify:NO];
848
+}
849
+
850
+- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
851
+    [self setSelectedSegmentIndex:index animated:animated notify:NO];
852
+}
853
+
854
+- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
855
+    _selectedSegmentIndex = index;
856
+    [self setNeedsDisplay];
857
+    
858
+    if (index == HMSegmentedControlNoSegment) {
859
+        [self.selectionIndicatorArrowLayer removeFromSuperlayer];
860
+        [self.selectionIndicatorStripLayer removeFromSuperlayer];
861
+        [self.selectionIndicatorBoxLayer removeFromSuperlayer];
862
+    } else {
863
+        [self scrollToSelectedSegmentIndex:animated];
864
+        
865
+        if (animated) {
866
+            // If the selected segment layer is not added to the super layer, that means no
867
+            // index is currently selected, so add the layer then move it to the new
868
+            // segment index without animating.
869
+            if(self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
870
+                if ([self.selectionIndicatorArrowLayer superlayer] == nil) {
871
+                    [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
872
+                    
873
+                    [self setSelectedSegmentIndex:index animated:NO notify:YES];
874
+                    return;
875
+                }
876
+            }else {
877
+                if ([self.selectionIndicatorStripLayer superlayer] == nil) {
878
+                    [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
879
+                    
880
+                    if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && [self.selectionIndicatorBoxLayer superlayer] == nil)
881
+                        [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
882
+                    
883
+                    [self setSelectedSegmentIndex:index animated:NO notify:YES];
884
+                    return;
885
+                }
886
+            }
887
+            
888
+            if (notify)
889
+                [self notifyForSegmentChangeToIndex:index];
890
+            
891
+            // Restore CALayer animations
892
+            self.selectionIndicatorArrowLayer.actions = nil;
893
+            self.selectionIndicatorStripLayer.actions = nil;
894
+            self.selectionIndicatorBoxLayer.actions = nil;
895
+            
896
+            // Animate to new position
897
+            [CATransaction begin];
898
+            [CATransaction setAnimationDuration:0.15f];
899
+            [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
900
+            [self setArrowFrame];
901
+            self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];
902
+            self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
903
+            self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
904
+            [CATransaction commit];
905
+        } else {
906
+            // Disable CALayer animations
907
+            NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
908
+            self.selectionIndicatorArrowLayer.actions = newActions;
909
+            [self setArrowFrame];
910
+            
911
+            self.selectionIndicatorStripLayer.actions = newActions;
912
+            self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
913
+            
914
+            self.selectionIndicatorBoxLayer.actions = newActions;
915
+            self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
916
+            
917
+            if (notify)
918
+                [self notifyForSegmentChangeToIndex:index];
919
+        }
920
+    }
921
+}
922
+
923
+- (void)notifyForSegmentChangeToIndex:(NSInteger)index {
924
+    if (self.superview)
925
+        [self sendActionsForControlEvents:UIControlEventValueChanged];
926
+    
927
+    if (self.indexChangeBlock)
928
+        self.indexChangeBlock(index);
929
+}
930
+
931
+#pragma mark - Styling Support
932
+
933
+- (NSDictionary *)resultingTitleTextAttributes {
934
+    NSDictionary *defaults = @{
935
+        NSFontAttributeName : [UIFont systemFontOfSize:19.0f],
936
+        NSForegroundColorAttributeName : [UIColor blackColor],
937
+    };
938
+    
939
+    NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:defaults];
940
+    
941
+    if (self.titleTextAttributes) {
942
+        [resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
943
+    }
944
+
945
+    return [resultingAttrs copy];
946
+}
947
+
948
+- (NSDictionary *)resultingSelectedTitleTextAttributes {
949
+    NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:[self resultingTitleTextAttributes]];
950
+    
951
+    if (self.selectedTitleTextAttributes) {
952
+        [resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
953
+    }
954
+    
955
+    return [resultingAttrs copy];
956
+}
957
+
958
+@end

lib/ios/RNNTabItemOptions.h → lib/ios/RNNBottomTabOptions.h View File

@@ -1,6 +1,6 @@
1
-#import <Foundation/Foundation.h>
1
+#import "RNNOptions.h"
2 2
 
3
-@interface RNNTabItemOptions : NSObject
3
+@interface RNNBottomTabOptions : RNNOptions
4 4
 
5 5
 @property (nonatomic) NSUInteger tag;
6 6
 @property (nonatomic, strong) NSString* title;
@@ -9,10 +9,4 @@
9 9
 @property (nonatomic, strong) NSNumber* visible;
10 10
 @property (nonatomic, strong) NSDictionary* icon;
11 11
 
12
--(instancetype)initWithDict:(NSDictionary*)tabItemDict;
13
-
14
--(void)mergeWith:(NSDictionary *)otherOptions;
15
-
16
--(void)resetOptions;
17
-
18 12
 @end

+ 51
- 0
lib/ios/RNNBottomTabOptions.m View File

@@ -0,0 +1,51 @@
1
+#import "RNNBottomTabOptions.h"
2
+
3
+@implementation RNNBottomTabOptions
4
+
5
+-(instancetype)initWithDict:(NSDictionary *)tabItemDict {
6
+	self = [super init];
7
+	
8
+	[self mergeWith:tabItemDict];
9
+	self.tag = [tabItemDict[@"tag"] integerValue];
10
+	
11
+	return self;
12
+}
13
+
14
+- (void)applyOn:(UIViewController *)viewController {
15
+	if (self.title || self.icon) {
16
+		UITabBarItem* tabItem = [[UITabBarItem alloc] initWithTitle:self.title image:[RCTConvert UIImage:self.icon] tag:self.tag];
17
+		tabItem.accessibilityIdentifier = self.testID;
18
+		[viewController.navigationController setTabBarItem:tabItem];
19
+	}
20
+	
21
+	if (self.badge) {
22
+		NSString *badge = [RCTConvert NSString:self.badge];
23
+		if (viewController.navigationController) {
24
+			viewController.navigationController.tabBarItem.badgeValue = badge;
25
+		} else {
26
+			viewController.tabBarItem.badgeValue = badge;
27
+		}
28
+	}
29
+	
30
+	if (self.visible) {
31
+		[viewController.tabBarController setSelectedIndex:[viewController.tabBarController.viewControllers indexOfObject:viewController]];
32
+	}
33
+	
34
+	[self resetOptions];
35
+}
36
+
37
+-(void)mergeWith:(NSDictionary *)otherOptions {
38
+	for (id key in otherOptions) {
39
+		[self setValue:[otherOptions objectForKey:key] forKey:key];
40
+	}
41
+}
42
+
43
+-(void)resetOptions {
44
+	self.title = nil;
45
+	self.badge = nil;
46
+	self.visible = nil;
47
+	self.icon = nil;
48
+	self.testID = nil;
49
+}
50
+
51
+@end

lib/ios/RNNTabBarOptions.h → lib/ios/RNNBottomTabsOptions.h View File

@@ -1,8 +1,6 @@
1
-#import <Foundation/Foundation.h>
1
+#import "RNNOptions.h"
2 2
 
3
-extern const NSInteger BLUR_TOPBAR_TAG;
4
-
5
-@interface RNNTabBarOptions : NSObject
3
+@interface RNNBottomTabsOptions : RNNOptions
6 4
 
7 5
 @property (nonatomic, strong) NSNumber* hidden;
8 6
 @property (nonatomic, strong) NSNumber* animateHide;
@@ -11,9 +9,4 @@ extern const NSInteger BLUR_TOPBAR_TAG;
11 9
 @property (nonatomic, strong) NSNumber* drawUnder;
12 10
 @property (nonatomic, strong) NSString* currentTabId;
13 11
 
14
--(instancetype)init;
15
--(instancetype)initWithDict:(NSDictionary *)topBarOptions;
16
--(void)mergeWith:(NSDictionary*)otherOptions;
17
-- (void)resetOptions;
18
-
19 12
 @end

+ 45
- 0
lib/ios/RNNBottomTabsOptions.m View File

@@ -0,0 +1,45 @@
1
+#import "RNNBottomTabsOptions.h"
2
+#import "RNNTabBarController.h"
3
+extern const NSInteger BLUR_TOPBAR_TAG;
4
+
5
+@implementation RNNBottomTabsOptions
6
+
7
+- (void)applyOn:(UIViewController *)viewController {
8
+	if (self.currentTabIndex) {
9
+		[viewController.tabBarController setSelectedIndex:[self.currentTabIndex unsignedIntegerValue]];
10
+	}
11
+	
12
+	if (self.currentTabId) {
13
+		[(RNNTabBarController*)viewController.tabBarController setSelectedIndexByContainerID:self.currentTabId];
14
+	}
15
+	
16
+	if (self.hidden) {
17
+		[((RNNTabBarController *)viewController.tabBarController) setTabBarHidden:[self.hidden boolValue] animated:[self.animateHide boolValue]];
18
+	}
19
+	
20
+	if (self.testID) {
21
+		viewController.tabBarController.tabBar.accessibilityIdentifier = self.testID;
22
+	}
23
+	
24
+	if (self.drawUnder) {
25
+		if ([self.drawUnder boolValue]) {
26
+			viewController.edgesForExtendedLayout |= UIRectEdgeBottom;
27
+		} else {
28
+			viewController.edgesForExtendedLayout &= ~UIRectEdgeBottom;
29
+		}
30
+	}
31
+	
32
+	[self resetOptions];
33
+}
34
+
35
+- (void)resetOptions {
36
+	self.currentTabId = nil;
37
+	self.currentTabIndex = nil;
38
+}
39
+
40
+-(void)mergeWith:(NSDictionary *)otherOptions {
41
+	for (id key in otherOptions) {
42
+		[self setValue:[otherOptions objectForKey:key] forKey:key];
43
+	}
44
+}
45
+@end

+ 10
- 0
lib/ios/RNNBridgeManager.h View File

@@ -0,0 +1,10 @@
1
+#import <Foundation/Foundation.h>
2
+#import <React/RCTBridge.h>
3
+
4
+@interface RNNBridgeManager : NSObject <RCTBridgeDelegate>
5
+
6
+- (instancetype)initWithJsCodeLocation:(NSURL *)jsCodeLocation launchOptions:(NSDictionary *)launchOptions;
7
+
8
+@property (readonly, nonatomic, strong) RCTBridge *bridge;
9
+
10
+@end

+ 85
- 0
lib/ios/RNNBridgeManager.m View File

@@ -0,0 +1,85 @@
1
+#import "RNNBridgeManager.h"
2
+
3
+#import <React/RCTBridge.h>
4
+#import <React/RCTUIManager.h>
5
+
6
+#import "RNNEventEmitter.h"
7
+#import "RNNSplashScreen.h"
8
+#import "RNNBridgeModule.h"
9
+#import "RNNRootViewCreator.h"
10
+#import "RNNReactRootViewCreator.h"
11
+
12
+@interface RNNBridgeManager() <RCTBridgeDelegate>
13
+
14
+@property (nonatomic, strong, readwrite) RCTBridge *bridge;
15
+
16
+@end
17
+
18
+@implementation RNNBridgeManager {
19
+	NSURL* _jsCodeLocation;
20
+	NSDictionary* _launchOptions;
21
+	RCTBridge* _bridge;
22
+
23
+	RNNStore* _store;
24
+
25
+	RNNCommandsHandler* _commandsHandler;
26
+}
27
+
28
+- (instancetype)initWithJsCodeLocation:(NSURL *)jsCodeLocation launchOptions:(NSDictionary *)launchOptions {
29
+	if (self = [super init]) {
30
+		_jsCodeLocation = jsCodeLocation;
31
+		_launchOptions = launchOptions;
32
+		
33
+		_store = [RNNStore new];
34
+		_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:_launchOptions];
35
+
36
+		[[NSNotificationCenter defaultCenter] addObserver:self
37
+												 selector:@selector(onJavaScriptLoaded)
38
+													 name:RCTJavaScriptDidLoadNotification
39
+												   object:nil];
40
+		[[NSNotificationCenter defaultCenter] addObserver:self
41
+												 selector:@selector(onJavaScriptWillLoad)
42
+													 name:RCTJavaScriptWillStartLoadingNotification
43
+												   object:nil];
44
+		[[NSNotificationCenter defaultCenter] addObserver:self
45
+												 selector:@selector(onBridgeWillReload)
46
+													 name:RCTBridgeWillReloadNotification
47
+												   object:nil];
48
+	}
49
+	return self;
50
+}
51
+
52
+# pragma mark - RCTBridgeDelegate
53
+
54
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
55
+	return _jsCodeLocation;
56
+}
57
+
58
+- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
59
+	RNNEventEmitter *eventEmitter = [[RNNEventEmitter alloc] init];
60
+
61
+	id<RNNRootViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge];
62
+	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator store:_store eventEmitter:eventEmitter andBridge:bridge];
63
+	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store controllerFactory:controllerFactory];
64
+	RNNBridgeModule *bridgeModule = [[RNNBridgeModule alloc] initWithCommandsHandler:_commandsHandler];
65
+
66
+	return @[bridgeModule,eventEmitter];
67
+}
68
+
69
+# pragma mark - JavaScript & Bridge Notifications
70
+
71
+- (void)onJavaScriptWillLoad {
72
+	[_store clean];
73
+}
74
+
75
+- (void)onJavaScriptLoaded {
76
+	[_store setReadyToReceiveCommands:true];
77
+	[[_bridge moduleForClass:[RNNEventEmitter class]] sendOnAppLaunched];
78
+}
79
+
80
+- (void)onBridgeWillReload {
81
+	UIApplication.sharedApplication.delegate.window.rootViewController =  nil;
82
+}
83
+
84
+@end
85
+

+ 1
- 1
lib/ios/RNNCommandsHandler.h View File

@@ -6,7 +6,7 @@
6 6
 
7 7
 @interface RNNCommandsHandler : NSObject
8 8
 
9
--(instancetype) initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory andBridge:(RCTBridge*)bridge;
9
+-(instancetype) initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory;
10 10
 
11 11
 -(void) setRoot:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion;
12 12
 

+ 3
- 7
lib/ios/RNNCommandsHandler.m View File

@@ -8,14 +8,12 @@
8 8
 @implementation RNNCommandsHandler {
9 9
 	RNNControllerFactory *_controllerFactory;
10 10
 	RNNStore *_store;
11
-	RCTBridge* _bridge;
12 11
 	RNNNavigationStackManager* _navigationStackManager;
13 12
 	RNNModalManager* _modalManager;
14 13
 }
15 14
 
16
--(instancetype) initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory andBridge:(RCTBridge*)bridge {
15
+-(instancetype) initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory {
17 16
 	self = [super init];
18
-	_bridge = bridge;
19 17
 	_store = store;
20 18
 	_controllerFactory = controllerFactory;
21 19
 	_navigationStackManager = [[RNNNavigationStackManager alloc] initWithStore:_store];
@@ -48,7 +46,6 @@
48 46
 		[CATransaction setCompletionBlock:completion];
49 47
 		
50 48
 		[rootVc.navigationOptions applyOn:vc];
51
-		[rootVc applyNavigationButtons];
52 49
 		
53 50
 		[CATransaction commit];
54 51
 	}
@@ -56,9 +53,8 @@
56 53
 
57 54
 -(void) push:(NSString*)containerId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion {
58 55
 	[self assertReady];
59
-	UIViewController *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
60
-	UIViewController *fromVc = [_store findContainerForId:containerId];
61
-	[_bridge.uiManager setAvailableSize:fromVc.view.bounds.size forRootView:newVc.view];
56
+	
57
+	UIViewController<RNNRootViewProtocol> *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
62 58
 	[_navigationStackManager push:newVc onTop:containerId completion:completion];
63 59
 }
64 60
 

+ 4
- 2
lib/ios/RNNControllerFactory.h View File

@@ -4,13 +4,15 @@
4 4
 #import "RNNRootViewCreator.h"
5 5
 #import "RNNStore.h"
6 6
 #import "RNNEventEmitter.h"
7
+#import "RNNRootViewProtocol.h"
7 8
 
8 9
 @interface RNNControllerFactory : NSObject
9 10
 
10 11
 -(instancetype)initWithRootViewCreator:(id <RNNRootViewCreator>)creator
11 12
 								 store:(RNNStore*)store
12
-						  eventEmitter:(RNNEventEmitter*)eventEmitter;
13
+						  eventEmitter:(RNNEventEmitter*)eventEmitter
14
+							 andBridge:(RCTBridge*)bridge;
13 15
 
14
--(UIViewController*)createLayoutAndSaveToStore:(NSDictionary*)layout;
16
+-(UIViewController<RNNRootViewProtocol> *)createLayoutAndSaveToStore:(NSDictionary*)layout;
15 17
 
16 18
 @end

+ 41
- 13
lib/ios/RNNControllerFactory.m View File

@@ -7,11 +7,13 @@
7 7
 #import "RNNNavigationOptions.h"
8 8
 #import "RNNNavigationController.h"
9 9
 #import "RNNTabBarController.h"
10
+#import "RNNTopTabsViewController.h"
10 11
 
11 12
 @implementation RNNControllerFactory {
12 13
 	id<RNNRootViewCreator> _creator;
13 14
 	RNNStore *_store;
14 15
 	RNNEventEmitter *_eventEmitter;
16
+	RCTBridge *_bridge;
15 17
 }
16 18
 
17 19
 # pragma mark public
@@ -19,28 +21,30 @@
19 21
 
20 22
 - (instancetype)initWithRootViewCreator:(id <RNNRootViewCreator>)creator
21 23
 								  store:(RNNStore *)store
22
-						   eventEmitter:(RNNEventEmitter*)eventEmitter {
24
+						   eventEmitter:(RNNEventEmitter*)eventEmitter
25
+							  andBridge:(RCTBridge *)bridge {
23 26
 	
24 27
 	self = [super init];
25 28
 	_creator = creator;
26 29
 	_store = store;
27 30
 	_eventEmitter = eventEmitter;
31
+	_bridge = bridge;
28 32
 	
29 33
 	return self;
30 34
 }
31 35
 
32
-- (UIViewController*)createLayoutAndSaveToStore:(NSDictionary*)layout {
36
+- (UIViewController<RNNRootViewProtocol> *)createLayoutAndSaveToStore:(NSDictionary*)layout {
33 37
 	return [self fromTree:layout];
34 38
 }
35 39
 
36 40
 # pragma mark private
37 41
 
38
-- (UIViewController*)fromTree:(NSDictionary*)json {
42
+- (UIViewController<RNNRootViewProtocol> *)fromTree:(NSDictionary*)json {
39 43
 	RNNLayoutNode* node = [RNNLayoutNode create:json];
40 44
 	
41
-	UIViewController* result;
45
+	UIViewController<RNNRootViewProtocol> *result;
42 46
 	
43
-	if ( node.isContainer) {
47
+	if ( node.isContainer || node.isTopTab) {
44 48
 		result = [self createContainer:node];
45 49
 	}
46 50
 	
@@ -52,6 +56,10 @@
52 56
 		result = [self createTabs:node];
53 57
 	}
54 58
 	
59
+	else if (node.isTopTabs) {
60
+		result = [self createTopTabs:node];
61
+	}
62
+	
55 63
 	else if (node.isSideMenuRoot) {
56 64
 		result = [self createSideMenu:node];
57 65
 	}
@@ -76,16 +84,20 @@
76 84
 	return result;
77 85
 }
78 86
 
79
-- (RNNRootViewController*)createContainer:(RNNLayoutNode*)node {
87
+- (UIViewController<RNNRootViewProtocol> *)createContainer:(RNNLayoutNode*)node {
80 88
 	NSString* name = node.data[@"name"];
81 89
 	NSDictionary* customTransition = node.data[@"customTransition"];
82 90
 	RNNAnimator* animator = [[RNNAnimator alloc] initWithAnimationsDictionary:customTransition];
83 91
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"navigationOptions"]];
84 92
 	NSString* containerId = node.nodeId;
85
-	return [[RNNRootViewController alloc] initWithName:name withOptions:options withContainerId:containerId rootViewCreator:_creator eventEmitter:_eventEmitter animator:animator];
93
+	RNNRootViewController* container = [[RNNRootViewController alloc] initWithName:name withOptions:options withContainerId:containerId rootViewCreator:_creator eventEmitter:_eventEmitter animator:animator];
94
+	CGSize availableSize = UIApplication.sharedApplication.delegate.window.bounds.size;
95
+	[_bridge.uiManager setAvailableSize:availableSize forRootView:container.view];
96
+	
97
+	return container;
86 98
 }
87 99
 
88
-- (RNNNavigationController*)createContainerStack:(RNNLayoutNode*)node {
100
+- (UIViewController<RNNRootViewProtocol> *)createContainerStack:(RNNLayoutNode*)node {
89 101
 	RNNNavigationController* vc = [[RNNNavigationController alloc] init];
90 102
 	
91 103
 	NSMutableArray* controllers = [NSMutableArray new];
@@ -97,12 +109,12 @@
97 109
 	return vc;
98 110
 }
99 111
 
100
--(RNNTabBarController*)createTabs:(RNNLayoutNode*)node {
112
+-(UIViewController<RNNRootViewProtocol> *)createTabs:(RNNLayoutNode*)node {
101 113
 	RNNTabBarController* vc = [[RNNTabBarController alloc] init];
102 114
 	
103 115
 	NSMutableArray* controllers = [NSMutableArray new];
104 116
 	for (NSDictionary *child in node.children) {
105
-		UIViewController* childVc = [self fromTree:child];
117
+		UIViewController* childVc = (UIViewController*)[self fromTree:child];
106 118
 		RNNRootViewController* rootView = (RNNRootViewController *)childVc.childViewControllers.firstObject;
107 119
 		[rootView applyTabBarItem];
108 120
 		
@@ -113,7 +125,23 @@
113 125
 	return vc;
114 126
 }
115 127
 
116
-- (UIViewController*)createSideMenu:(RNNLayoutNode*)node {
128
+- (UIViewController<RNNRootViewProtocol> *)createTopTabs:(RNNLayoutNode*)node {
129
+	RNNTopTabsViewController* vc = [[RNNTopTabsViewController alloc] init];
130
+	
131
+	NSMutableArray* controllers = [NSMutableArray new];
132
+	for (NSDictionary *child in node.children) {
133
+		RNNRootViewController* childVc = (RNNRootViewController*)[self fromTree:child];
134
+		childVc.topTabsViewController = vc;
135
+		[controllers addObject:childVc];
136
+		[_bridge.uiManager setAvailableSize:vc.contentView.bounds.size forRootView:childVc.view];
137
+	}
138
+	
139
+	[vc setViewControllers:controllers];
140
+	
141
+	return vc;
142
+}
143
+
144
+- (UIViewController<RNNRootViewProtocol> *)createSideMenu:(RNNLayoutNode*)node {
117 145
 	NSMutableArray* childrenVCs = [NSMutableArray new];
118 146
 	
119 147
 	
@@ -126,8 +154,8 @@
126 154
 }
127 155
 
128 156
 
129
-- (UIViewController*)createSideMenuChild:(RNNLayoutNode*)node type:(RNNSideMenuChildType)type {
130
-	UIViewController* child = [self fromTree:node.children[0]];
157
+- (UIViewController<RNNRootViewProtocol> *)createSideMenuChild:(RNNLayoutNode*)node type:(RNNSideMenuChildType)type {
158
+	UIViewController* child = (UIViewController*)[self fromTree:node.children[0]];
131 159
 	RNNSideMenuChildVC *sideMenuChild = [[RNNSideMenuChildVC alloc] initWithChild: child type:type];
132 160
 	
133 161
 	return sideMenuChild;

+ 2
- 0
lib/ios/RNNLayoutNode.h View File

@@ -14,6 +14,8 @@
14 14
 -(BOOL)isContainer;
15 15
 -(BOOL)isContainerStack;
16 16
 -(BOOL)isTabs;
17
+-(BOOL)isTopTabs;
18
+-(BOOL)isTopTab;
17 19
 -(BOOL)isSideMenuRoot;
18 20
 -(BOOL)isSideMenuLeft;
19 21
 -(BOOL)isSideMenuRight;

+ 8
- 0
lib/ios/RNNLayoutNode.m View File

@@ -25,6 +25,14 @@
25 25
 {
26 26
 	return [self.type isEqualToString:@"BottomTabs"];
27 27
 }
28
+-(BOOL)isTopTabs
29
+{
30
+	return [self.type isEqualToString:@"TopTabs"];
31
+}
32
+-(BOOL)isTopTab
33
+{
34
+	return [self.type isEqualToString:@"TopTab"];
35
+}
28 36
 -(BOOL)isSideMenuRoot
29 37
 {
30 38
 	return [self.type isEqualToString:@"SideMenuRoot"];

+ 0
- 1
lib/ios/RNNNavigationButtons.h View File

@@ -4,7 +4,6 @@
4 4
 
5 5
 @interface RNNNavigationButtons : NSObject
6 6
 
7
--(instancetype)init;
8 7
 -(instancetype)initWithViewController:(RNNRootViewController*)viewController;
9 8
 
10 9
 -(void)applyLeftButtons:(NSArray*)leftButtons rightButtons:(NSArray*)rightButtons;

+ 14
- 15
lib/ios/RNNNavigationButtons.m View File

@@ -4,16 +4,15 @@
4 4
 #import "RCTHelpers.h"
5 5
 
6 6
 @interface RNNNavigationButtons()
7
+
7 8
 @property (weak, nonatomic) RNNRootViewController* viewController;
9
+@property (strong, nonatomic) NSArray* rightButtons;
10
+@property (strong, nonatomic) NSArray* leftButtons;
8 11
 
9 12
 @end
10 13
 
11 14
 @implementation RNNNavigationButtons
12 15
 
13
--(instancetype)init {
14
-	return [super init];
15
-}
16
-
17 16
 -(instancetype)initWithViewController:(RNNRootViewController*)viewController {
18 17
 	self = [super init];
19 18
 	
@@ -23,11 +22,11 @@
23 22
 }
24 23
 
25 24
 -(void)applyLeftButtons:(NSArray*)leftButtons rightButtons:(NSArray*)rightButtons {
26
-	if(leftButtons) {
25
+	if (leftButtons) {
27 26
 		[self setButtons:leftButtons side:@"left" animated:NO];
28 27
 	}
29 28
 	
30
-	if(rightButtons) {
29
+	if (rightButtons) {
31 30
 		[self setButtons:rightButtons side:@"right" animated:NO];
32 31
 	}
33 32
 }
@@ -41,14 +40,14 @@
41 40
 		}
42 41
 	}
43 42
 	
44
-	if ([side isEqualToString:@"left"])
45
-	{
46
-		[self.viewController.navigationItem setLeftBarButtonItems:barButtonItems animated:animated];
43
+	if ([side isEqualToString:@"left"]) {
44
+		self.leftButtons = barButtonItems;
45
+		[self.viewController.navigationItem setLeftBarButtonItems:self.leftButtons animated:animated];
47 46
 	}
48 47
 	
49
-	if ([side isEqualToString:@"right"])
50
-	{
51
-		[self.viewController.navigationItem setRightBarButtonItems:barButtonItems animated:animated];
48
+	if ([side isEqualToString:@"right"]) {
49
+		self.rightButtons = barButtonItems;
50
+		[self.viewController.navigationItem setRightBarButtonItems:self.rightButtons animated:animated];
52 51
 	}
53 52
 }
54 53
 
@@ -56,7 +55,7 @@
56 55
 	NSString* buttonId = dictionary[@"id"];
57 56
 	NSString* title = dictionary[@"title"];
58 57
 	
59
-	if(!buttonId){
58
+	if (!buttonId) {
60 59
 		@throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:[@"button id is not specified " stringByAppendingString:title] userInfo:nil];
61 60
 	}
62 61
 	
@@ -104,8 +103,8 @@
104 103
 	return barButtonItem;
105 104
 }
106 105
 
107
--(void)onButtonPress:(RNNUIBarButtonItem*)barButtonItem
108
-{
106
+-(void)onButtonPress:(RNNUIBarButtonItem*)barButtonItem {
109 107
 	[self.viewController.eventEmitter sendOnNavigationButtonPressed:self.viewController.containerId buttonId:barButtonItem.buttonId];
110 108
 }
109
+
111 110
 @end

+ 2
- 1
lib/ios/RNNNavigationController.h View File

@@ -1,5 +1,6 @@
1 1
 #import <UIKit/UIKit.h>
2
+#import "RNNRootViewProtocol.h"
2 3
 
3
-@interface RNNNavigationController : UINavigationController
4
+@interface RNNNavigationController : UINavigationController <RNNRootViewProtocol>
4 5
 
5 6
 @end

+ 4
- 0
lib/ios/RNNNavigationController.m View File

@@ -7,4 +7,8 @@
7 7
 	return self.viewControllers.lastObject.supportedInterfaceOrientations;
8 8
 }
9 9
 
10
+- (BOOL)isCustomTransitioned {
11
+	return NO;
12
+}
13
+
10 14
 @end

+ 12
- 18
lib/ios/RNNNavigationOptions.h View File

@@ -1,43 +1,37 @@
1 1
 #import <Foundation/Foundation.h>
2
-#import <UIKit/UIKit.h>
3 2
 #import "RNNTopBarOptions.h"
4
-#import "RNNTabBarOptions.h"
3
+#import "RNNBottomTabsOptions.h"
4
+#import "RNNBottomTabOptions.h"
5 5
 #import "RNNSideMenuOptions.h"
6
-#import "RNNTabItemOptions.h"
6
+#import "RNNTopTabOptions.h"
7
+#import "RNNTopTabsOptions.h"
7 8
 
8 9
 extern const NSInteger BLUR_STATUS_TAG;
9 10
 extern const NSInteger BLUR_TOPBAR_TAG;
10 11
 extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
11 12
 
12
-@interface RNNNavigationOptions : NSObject
13
+@interface RNNNavigationOptions : RNNOptions
14
+
15
+@property (nonatomic, strong) RNNTopBarOptions* topBar;
16
+@property (nonatomic, strong) RNNBottomTabsOptions* bottomTabs;
17
+@property (nonatomic, strong) RNNBottomTabOptions* bottomTab;
18
+@property (nonatomic, strong) RNNTopTabsOptions* topTabs;
19
+@property (nonatomic, strong) RNNTopTabOptions* topTab;
20
+@property (nonatomic, strong) RNNSideMenuOptions* sideMenu;
13 21
 
14 22
 @property (nonatomic, strong) NSNumber* statusBarHidden;
15 23
 @property (nonatomic, strong) NSNumber* screenBackgroundColor;
16 24
 @property (nonatomic, strong) NSMutableDictionary* originalTopBarImages;
17 25
 @property (nonatomic, strong) NSString* backButtonTransition;
18 26
 @property (nonatomic, strong) id orientation;
19
-@property (nonatomic, strong) NSArray* leftButtons;
20
-@property (nonatomic, strong) NSArray* rightButtons;
21 27
 @property (nonatomic, strong) NSNumber* statusBarBlur;
22 28
 @property (nonatomic, strong) NSNumber* statusBarHideWithTopBar;
23 29
 @property (nonatomic, strong) NSNumber* popGesture;
24
-@property (nonatomic, strong) RNNTopBarOptions* topBar;
25
-@property (nonatomic, strong) RNNTabBarOptions* bottomTabs;
26
-@property (nonatomic, strong) RNNSideMenuOptions* sideMenu;
27 30
 @property (nonatomic, strong) UIImage* backgroundImage;
28 31
 @property (nonatomic, strong) UIImage* rootBackgroundImage;
29
-@property (nonatomic, strong) RNNTabItemOptions* tabItem;
30 32
 
31 33
 
32 34
 - (UIInterfaceOrientationMask)supportedOrientations;
33 35
 
34
--(instancetype)init;
35
--(instancetype)initWithDict:(NSDictionary *)navigationOptions;
36
-
37
--(void)applyOn:(UIViewController*)viewController;
38
--(void)mergeWith:(NSDictionary*)otherOptions;
39
--(void)storeOriginalTopBarImages:(UIViewController*)viewController;
40
-
41
-- (void)applyTabBarItemOptions:(UIViewController*)viewController;
42 36
 
43 37
 @end

+ 13
- 243
lib/ios/RNNNavigationOptions.m View File

@@ -4,6 +4,8 @@
4 4
 #import "RNNTabBarController.h"
5 5
 #import "RNNTopBarOptions.h"
6 6
 #import "RNNSideMenuController.h"
7
+#import "RNNRootViewController.h"
8
+#import "RNNNavigationButtons.h"
7 9
 
8 10
 const NSInteger BLUR_STATUS_TAG = 78264801;
9 11
 const NSInteger BLUR_TOPBAR_TAG = 78264802;
@@ -11,8 +13,6 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
11 13
 
12 14
 @implementation RNNNavigationOptions
13 15
 
14
-
15
-
16 16
 -(instancetype)init {
17 17
 	return [self initWithDict:@{}];
18 18
 }
@@ -23,28 +23,22 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
23 23
 	self.screenBackgroundColor = [navigationOptions objectForKey:@"screenBackgroundColor"];
24 24
 	self.backButtonTransition = [navigationOptions objectForKey:@"backButtonTransition"];
25 25
 	self.orientation = [navigationOptions objectForKey:@"orientation"];
26
-	self.leftButtons = [navigationOptions objectForKey:@"leftButtons"];
27
-	self.rightButtons = [navigationOptions objectForKey:@"rightButtons"];
28 26
 	self.topBar = [[RNNTopBarOptions alloc] initWithDict:[navigationOptions objectForKey:@"topBar"]];
29
-	self.bottomTabs = [[RNNTabBarOptions alloc] initWithDict:[navigationOptions objectForKey:@"bottomTabs"]];
27
+	self.topTab = [[RNNTopTabOptions alloc] initWithDict:[navigationOptions objectForKey:@"topTab"]];
28
+	self.bottomTabs = [[RNNBottomTabsOptions alloc] initWithDict:[navigationOptions objectForKey:@"bottomTabs"]];
30 29
 	self.sideMenu = [[RNNSideMenuOptions alloc] initWithDict:[navigationOptions objectForKey:@"sideMenu"]];
31 30
 	self.backgroundImage = [RCTConvert UIImage:[navigationOptions objectForKey:@"backgroundImage"]];
32 31
 	self.rootBackgroundImage = [RCTConvert UIImage:[navigationOptions objectForKey:@"rootBackgroundImage"]];
33
-	self.tabItem = [[RNNTabItemOptions alloc] initWithDict:[navigationOptions objectForKey:@"bottomTab"]];
32
+	self.bottomTab = [[RNNBottomTabOptions alloc] initWithDict:[navigationOptions objectForKey:@"bottomTab"]];
34 33
     
35 34
 	return self;
36 35
 }
37 36
 
38 37
 -(void)mergeWith:(NSDictionary *)otherOptions {
39 38
 	for (id key in otherOptions) {
40
-		if ([key isEqualToString:@"topBar"]) {
41
-			[self.topBar mergeWith:[otherOptions objectForKey:key]];
42
-		} else if ([key isEqualToString:@"bottomTabs"]) {
43
-			[self.bottomTabs mergeWith:[otherOptions objectForKey:key]];
44
-		} else if ([key isEqualToString:@"sideMenu"]) {
45
-			[self.sideMenu mergeWith:[otherOptions objectForKey:@"sideMenu"]];
46
-		} else if ([key isEqualToString:@"bottomTab"]) {
47
-			[self.tabItem mergeWith:[otherOptions objectForKey:key]];
39
+		if ([[self valueForKey:key] isKindOfClass:[RNNOptions class]]) {
40
+			RNNOptions* options = [self valueForKey:key];
41
+			[options mergeWith:[otherOptions objectForKey:key]];
48 42
 		} else {
49 43
 			[self setValue:[otherOptions objectForKey:key] forKey:key];
50 44
 		}
@@ -52,145 +46,11 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
52 46
 }
53 47
 
54 48
 -(void)applyOn:(UIViewController*)viewController {
55
-	if (self.topBar) {
56
-		if (self.topBar.backgroundColor) {
57
-			UIColor* backgroundColor = [RCTConvert UIColor:self.topBar.backgroundColor];
58
-			viewController.navigationController.navigationBar.barTintColor = backgroundColor;
59
-		} else {
60
-			viewController.navigationController.navigationBar.barTintColor = nil;
61
-		}
62
-		
63
-		if (self.topBar.title) {
64
-			viewController.navigationItem.title = self.topBar.title;
65
-		}
66
-		
67
-		if (@available(iOS 11.0, *)) {
68
-			if (self.topBar.largeTitle){
69
-				if ([self.topBar.largeTitle boolValue]) {
70
-					viewController.navigationController.navigationBar.prefersLargeTitles = YES;
71
-					viewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlways;
72
-				} else {
73
-					viewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
74
-				}
75
-			} else {
76
-				viewController.navigationController.navigationBar.prefersLargeTitles = NO;
77
-				viewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
78
-			}
79
-		}
80
-		
81
-		
82
-		if (self.topBar.textFontFamily || self.topBar.textFontSize || self.topBar.textColor) {
83
-			NSMutableDictionary* navigationBarTitleTextAttributes = [NSMutableDictionary new];
84
-			if (self.topBar.textColor) {
85
-				navigationBarTitleTextAttributes[NSForegroundColorAttributeName] = [RCTConvert UIColor:[self.topBar valueForKey:@"textColor"]];
86
-			}
87
-			if (self.topBar.textFontFamily){
88
-				if(self.topBar.textFontSize) {
89
-					navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.topBar.textFontFamily size:[self.topBar.textFontSize floatValue]];
90
-				} else {
91
-					navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.topBar.textFontFamily size:20];
92
-				}
93
-			} else if (self.topBar.textFontSize) {
94
-				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont systemFontOfSize:[self.topBar.textFontSize floatValue]];
95
-			}
96
-			viewController.navigationController.navigationBar.titleTextAttributes = navigationBarTitleTextAttributes;
97
-			if (@available(iOS 11.0, *)){
98
-				viewController.navigationController.navigationBar.largeTitleTextAttributes = navigationBarTitleTextAttributes;
99
-			}
100
-			
101
-		}
102
-		
103
-		
104
-		if (self.topBar.hidden){
105
-			[viewController.navigationController setNavigationBarHidden:[self.topBar.hidden boolValue] animated:[self.topBar.animateHide boolValue]];
106
-		}
107
-		
108
-		if (self.topBar.hideOnScroll) {
109
-			viewController.navigationController.hidesBarsOnSwipe = [self.topBar.hideOnScroll boolValue];
110
-		}
111
-		
112
-		if (self.topBar.buttonColor) {
113
-			UIColor* buttonColor = [RCTConvert UIColor:self.topBar.buttonColor];
114
-			viewController.navigationController.navigationBar.tintColor = buttonColor;
115
-		} else {
116
-			viewController.navigationController.navigationBar.tintColor = nil;
117
-		}
118
-		
119
-		if ([self.topBar.blur boolValue]) {
120
-			if (![viewController.navigationController.navigationBar viewWithTag:BLUR_TOPBAR_TAG]) {
121
-				
122
-				[viewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
123
-				viewController.navigationController.navigationBar.shadowImage = [UIImage new];
124
-				UIVisualEffectView *blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
125
-				CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
126
-				blur.frame = CGRectMake(0, -1 * statusBarFrame.size.height, viewController.navigationController.navigationBar.frame.size.width, viewController.navigationController.navigationBar.frame.size.height + statusBarFrame.size.height);
127
-				blur.userInteractionEnabled = NO;
128
-				blur.tag = BLUR_TOPBAR_TAG;
129
-				[viewController.navigationController.navigationBar insertSubview:blur atIndex:0];
130
-				[viewController.navigationController.navigationBar sendSubviewToBack:blur];
131
-			}
132
-		} else {
133
-			UIView *blur = [viewController.navigationController.navigationBar viewWithTag:BLUR_TOPBAR_TAG];
134
-			if (blur) {
135
-				[viewController.navigationController.navigationBar setBackgroundImage: nil forBarMetrics:UIBarMetricsDefault];
136
-				viewController.navigationController.navigationBar.shadowImage = nil;
137
-				[blur removeFromSuperview];
138
-			}
139
-		}
140
-		
141
-		void (^disableTopBarTransparent)() = ^void(){
142
-			UIView *transparentView = [viewController.navigationController.navigationBar viewWithTag:TOP_BAR_TRANSPARENT_TAG];
143
-			if (transparentView){
144
-				[transparentView removeFromSuperview];
145
-				[viewController.navigationController.navigationBar setBackgroundImage:self.originalTopBarImages[@"backgroundImage"] forBarMetrics:UIBarMetricsDefault];
146
-				viewController.navigationController.navigationBar.shadowImage = self.originalTopBarImages[@"shadowImage"];
147
-				self.originalTopBarImages = nil;
148
-			}
149
-		};
150
-	
151
-		if (self.topBar.transparent) {
152
-			if ([self.topBar.transparent boolValue]) {
153
-				if (![viewController.navigationController.navigationBar viewWithTag:TOP_BAR_TRANSPARENT_TAG]){
154
-					[self storeOriginalTopBarImages:viewController];
155
-					[viewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
156
-					viewController.navigationController.navigationBar.shadowImage = [UIImage new];
157
-					UIView *transparentView = [[UIView alloc] initWithFrame:CGRectZero];
158
-					transparentView.tag = TOP_BAR_TRANSPARENT_TAG;
159
-					[viewController.navigationController.navigationBar insertSubview:transparentView atIndex:0];
160
-				}
161
-			} else {
162
-				disableTopBarTransparent();
163
-			}
164
-		} else {
165
-			disableTopBarTransparent();
166
-		}
167
-
168
-		if (self.topBar.translucent) {
169
-			viewController.navigationController.navigationBar.translucent = [self.topBar.translucent boolValue];
170
-		}
171
-
172
-		if (self.topBar.drawUnder) {
173
-			if ([self.topBar.drawUnder boolValue]) {
174
-				viewController.edgesForExtendedLayout |= UIRectEdgeTop;
175
-			} else {
176
-				viewController.edgesForExtendedLayout &= ~UIRectEdgeTop;
177
-			}
178
-		}
179
-		
180
-		if (self.topBar.noBorder) {
181
-			if ([self.topBar.noBorder boolValue]) {
182
-				viewController.navigationController.navigationBar
183
-				.shadowImage = [[UIImage alloc] init];
184
-			} else {
185
-				viewController.navigationController.navigationBar
186
-				.shadowImage = nil;
187
-			}
188
-		}
189
-		
190
-		if (self.topBar.testID) {
191
-			viewController.navigationController.navigationBar.accessibilityIdentifier = self.topBar.testID;
192
-		}
193
-	}
49
+	[self.topBar applyOn:viewController];
50
+	[self.bottomTabs applyOn:viewController];
51
+	[self.topTab applyOn:viewController];
52
+	[self.bottomTab applyOn:viewController];
53
+	[self.sideMenu applyOn:viewController];
194 54
 	
195 55
 	if (self.popGesture) {
196 56
 		viewController.navigationController.interactivePopGestureRecognizer.enabled = [self.popGesture boolValue];
@@ -201,34 +61,6 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
201 61
 		viewController.view.backgroundColor = screenColor;
202 62
 	}
203 63
 	
204
-	if (self.bottomTabs) {
205
-		if (self.bottomTabs.currentTabIndex) {
206
-			[viewController.tabBarController setSelectedIndex:[self.bottomTabs.currentTabIndex unsignedIntegerValue]];
207
-		}
208
-		
209
-		if (self.bottomTabs.currentTabId) {
210
-			[(RNNTabBarController*)viewController.tabBarController setSelectedIndexByContainerID:self.bottomTabs.currentTabId];
211
-		}
212
-		
213
-		if (self.bottomTabs.hidden) {
214
-			[((RNNTabBarController *)viewController.tabBarController) setTabBarHidden:[self.bottomTabs.hidden boolValue] animated:[self.bottomTabs.animateHide boolValue]];
215
-		}
216
-		
217
-		if (self.bottomTabs.testID) {
218
-			viewController.tabBarController.tabBar.accessibilityIdentifier = self.bottomTabs.testID;
219
-		}
220
-		
221
-		if (self.bottomTabs.drawUnder) {
222
-			if ([self.bottomTabs.drawUnder boolValue]) {
223
-				viewController.edgesForExtendedLayout |= UIRectEdgeBottom;
224
-			} else {
225
-				viewController.edgesForExtendedLayout &= ~UIRectEdgeBottom;
226
-			}
227
-		}
228
-		
229
-		[self.bottomTabs resetOptions];
230
-	}
231
-	
232 64
 	if (self.statusBarBlur) {
233 65
 		UIView* curBlurView = [viewController.view viewWithTag:BLUR_STATUS_TAG];
234 66
 		if ([self.statusBarBlur boolValue]) {
@@ -245,27 +77,6 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
245 77
 		}
246 78
 	}
247 79
 	
248
-	RNNSideMenuController* sideMenuController = (RNNSideMenuController*)UIApplication.sharedApplication.delegate.window.rootViewController;
249
-	if ([sideMenuController isKindOfClass:[RNNSideMenuController class]]) {
250
-		if (self.sideMenu.leftSideVisible) {
251
-			if (self.sideMenu.leftSideVisible.boolValue) {
252
-				[sideMenuController showSideMenu:MMDrawerSideLeft animated:YES];
253
-			} else {
254
-				[sideMenuController hideSideMenu:MMDrawerSideLeft animated:YES];
255
-			}
256
-		}
257
-		
258
-		if (self.sideMenu.rightSideVisible) {
259
-			if (self.sideMenu.rightSideVisible.boolValue) {
260
-				[sideMenuController showSideMenu:MMDrawerSideRight animated:YES];
261
-			} else {
262
-				[sideMenuController hideSideMenu:MMDrawerSideRight animated:YES];
263
-			}
264
-		}
265
-		
266
-		[self.sideMenu resetOptions];
267
-	}
268
-	
269 80
 	if (self.backgroundImage) {
270 81
 		UIImageView* backgroundImageView = (viewController.view.subviews.count > 0) ? viewController.view.subviews[0] : nil;
271 82
 		if (![backgroundImageView isKindOfClass:[UIImageView class]]) {
@@ -289,33 +100,6 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
289 100
 		backgroundImageView.image = self.rootBackgroundImage;
290 101
 		[backgroundImageView setContentMode:UIViewContentModeScaleAspectFill];
291 102
 	}
292
-	
293
-	[self applyTabBarItemOptions:viewController];
294
-}
295
-
296
-- (void)applyTabBarItemOptions:(UIViewController*)viewController {
297
-	if (self.tabItem) {
298
-		if (self.tabItem.title || self.tabItem.icon) {
299
-			UITabBarItem* tabItem = [[UITabBarItem alloc] initWithTitle:self.tabItem.title image:[RCTConvert UIImage:self.tabItem.icon] tag:self.tabItem.tag];
300
-			tabItem.accessibilityIdentifier = self.tabItem.testID;
301
-			[viewController.navigationController setTabBarItem:tabItem];
302
-		}
303
-		
304
-		if (self.tabItem.badge) {
305
-			NSString *badge = [RCTConvert NSString:self.tabItem.badge];
306
-			if (viewController.navigationController) {
307
-				viewController.navigationController.tabBarItem.badgeValue = badge;
308
-			} else {
309
-				viewController.tabBarItem.badgeValue = badge;
310
-			}
311
-		}
312
-		
313
-		if (self.tabItem.visible) {
314
-			[viewController.tabBarController setSelectedIndex:[viewController.tabBarController.viewControllers indexOfObject:viewController]];
315
-		}
316
-		
317
-		[self.tabItem resetOptions];
318
-	}
319 103
 }
320 104
 
321 105
 - (UIInterfaceOrientationMask)supportedOrientations {
@@ -344,18 +128,4 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
344 128
 	return supportedOrientationsMask;
345 129
 }
346 130
 
347
--(void)storeOriginalTopBarImages:(UIViewController*)viewController {
348
-	NSMutableDictionary *originalTopBarImages = [@{} mutableCopy];
349
-	UIImage *bgImage = [viewController.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault];
350
-	if (bgImage != nil) {
351
-		originalTopBarImages[@"backgroundImage"] = bgImage;
352
-	}
353
-	UIImage *shadowImage = viewController.navigationController.navigationBar.shadowImage;
354
-	if (shadowImage != nil) {
355
-		originalTopBarImages[@"shadowImage"] = shadowImage;
356
-	}
357
-	self.originalTopBarImages = originalTopBarImages;
358
-}
359
-
360
-
361 131
 @end

+ 2
- 2
lib/ios/RNNNavigationStackManager.h View File

@@ -5,12 +5,12 @@
5 5
 @interface RNNNavigationStackManager : NSObject
6 6
 
7 7
 @property (nonatomic, strong) UIViewController* fromVC;
8
-@property (nonatomic, strong) RNNRootViewController* toVC;
8
+@property (nonatomic, strong) UIViewController<RNNRootViewProtocol>* toVC;
9 9
 @property (nonatomic) int loadCount;
10 10
 -(instancetype)initWithStore:(RNNStore*)store;
11 11
 
12 12
 
13
--(void)push:(UIViewController*)newTop onTop:(NSString*)containerId completion:(RNNTransitionCompletionBlock)completion;
13
+-(void)push:(UIViewController<RNNRootViewProtocol>*)newTop onTop:(NSString*)containerId completion:(RNNTransitionCompletionBlock)completion;
14 14
 -(void)pop:(NSString*)containerId withAnimationData:(NSDictionary*)animationData;
15 15
 -(void)popTo:(NSString*)containerId;
16 16
 -(void)popToRoot:(NSString*)containerId;

+ 5
- 6
lib/ios/RNNNavigationStackManager.m View File

@@ -15,19 +15,18 @@ dispatch_queue_t RCTGetUIManagerQueue(void);
15 15
 	return self;
16 16
 }
17 17
 
18
--(void)push:(UIViewController *)newTop onTop:(NSString *)containerId completion:(RNNTransitionCompletionBlock)completion {
18
+-(void)push:(UIViewController<RNNRootViewProtocol> *)newTop onTop:(NSString *)containerId completion:(RNNTransitionCompletionBlock)completion {
19 19
 	UIViewController *vc = [_store findContainerForId:containerId];
20 20
 	[self preparePush:newTop onTopVC:vc completion:completion];
21 21
 	[self waitForContentToAppearAndThen:@selector(pushAfterLoad:)];
22 22
 }
23 23
 
24
--(void)preparePush:(UIViewController *)newTop onTopVC:(UIViewController*)vc completion:(RNNTransitionCompletionBlock)completion {
25
-	self.toVC = (RNNRootViewController*)newTop;
24
+-(void)preparePush:(UIViewController<RNNRootViewProtocol> *)newTop onTopVC:(UIViewController*)vc completion:(RNNTransitionCompletionBlock)completion {
25
+	self.toVC = newTop;
26 26
 	self.fromVC = vc;
27 27
 	
28
-	if (self.toVC.isAnimated) {
29
-		RNNRootViewController* newTopRootView = (RNNRootViewController*)newTop;
30
-		vc.navigationController.delegate = newTopRootView;
28
+	if (self.toVC.isCustomTransitioned) {
29
+		vc.navigationController.delegate = newTop;
31 30
 	} else {
32 31
 		vc.navigationController.delegate = nil;
33 32
 		self.fromVC.navigationController.interactivePopGestureRecognizer.delegate = nil;

+ 17
- 0
lib/ios/RNNOptions.h View File

@@ -0,0 +1,17 @@
1
+#import <Foundation/Foundation.h>
2
+#import <UIKit/UIKit.h>
3
+#import <React/RCTConvert.h>
4
+@protocol RNNOptionsProtocol <NSObject>
5
+
6
+@optional
7
+-(void)resetOptions;
8
+-(void)applyOn:(UIViewController*)viewController;
9
+
10
+@end
11
+
12
+@interface RNNOptions : NSObject <RNNOptionsProtocol>
13
+
14
+-(instancetype)initWithDict:(NSDictionary*)dict;
15
+-(void)mergeWith:(NSDictionary*)otherOptions;
16
+
17
+@end

+ 18
- 0
lib/ios/RNNOptions.m View File

@@ -0,0 +1,18 @@
1
+
2
+#import "RNNOptions.h"
3
+
4
+@implementation RNNOptions
5
+
6
+-(instancetype)initWithDict:(NSDictionary *)dict {
7
+	self = [super init];
8
+	[self mergeWith:dict];
9
+	return self;
10
+}
11
+
12
+-(void)mergeWith:(NSDictionary *)otherOptions {
13
+	for (id key in otherOptions) {
14
+		[self setValue:[otherOptions objectForKey:key] forKey:key];
15
+	}
16
+}
17
+
18
+@end

+ 8
- 3
lib/ios/RNNRootViewController.h View File

@@ -6,12 +6,16 @@
6 6
 #import "RNNEventEmitter.h"
7 7
 #import "RNNNavigationOptions.h"
8 8
 #import "RNNAnimator.h"
9
+#import "RNNTopTabsViewController.h"
10
+#import "RNNRootViewProtocol.h"
11
+
12
+@interface RNNRootViewController : UIViewController	<RNNRootViewProtocol>
9 13
 
10
-@interface RNNRootViewController : UIViewController	<UINavigationControllerDelegate>
11 14
 @property (nonatomic, strong) RNNNavigationOptions* navigationOptions;
12 15
 @property (nonatomic, strong) RNNAnimator* animator;
13 16
 @property (nonatomic, strong) RNNEventEmitter *eventEmitter;
14 17
 @property (nonatomic, strong) NSString* containerId;
18
+@property (nonatomic, strong) RNNTopTabsViewController* topTabsViewController;
15 19
 
16 20
 -(instancetype)initWithName:(NSString*)name
17 21
 				withOptions:(RNNNavigationOptions*)options
@@ -21,8 +25,9 @@
21 25
 		   animator:(RNNAnimator*)animator;
22 26
 
23 27
 
24
--(void)applyNavigationButtons;
25
--(BOOL)isAnimated;
26 28
 -(void)applyTabBarItem;
29
+-(void)applyTopTabsOptions;
30
+
31
+-(BOOL)isCustomTransitioned;
27 32
 
28 33
 @end

+ 6
- 11
lib/ios/RNNRootViewController.m View File

@@ -2,12 +2,10 @@
2 2
 #import "RNNRootViewController.h"
3 3
 #import <React/RCTConvert.h>
4 4
 #import "RNNAnimator.h"
5
-#import "RNNNavigationButtons.h"
6 5
 
7 6
 @interface RNNRootViewController()
8 7
 @property (nonatomic, strong) NSString* containerName;
9 8
 @property (nonatomic) BOOL _statusBarHidden;
10
-@property (nonatomic, strong) RNNNavigationButtons* navigationButtons;
11 9
 
12 10
 @end
13 11
 
@@ -33,21 +31,20 @@
33 31
 											   object:nil];
34 32
 	self.navigationController.modalPresentationStyle = UIModalPresentationCustom;
35 33
 	self.navigationController.delegate = self;
36
-	self.navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self];
34
+
37 35
 	return self;
38 36
 }
39 37
 	
40 38
 -(void)viewWillAppear:(BOOL)animated{
41 39
 	[super viewWillAppear:animated];
42 40
 	[self.navigationOptions applyOn:self];
43
-	[self applyNavigationButtons];
44 41
 }
45 42
 
46 43
 - (void)viewDidLoad {
47 44
 	[super viewDidLoad];
48 45
 }
49 46
 
50
--(BOOL)isAnimated {
47
+-(BOOL)isCustomTransitioned {
51 48
 	return self.animator;
52 49
 }
53 50
 
@@ -106,14 +103,12 @@
106 103
 
107 104
 }
108 105
 
109
-
110
-
111
--(void)applyNavigationButtons{
112
-	[self.navigationButtons applyLeftButtons:self.navigationOptions.leftButtons rightButtons:self.navigationOptions.rightButtons];
106
+-(void)applyTabBarItem {
107
+	[self.navigationOptions.bottomTab applyOn:self];
113 108
 }
114 109
 
115
--(void)applyTabBarItem {
116
-	[self.navigationOptions applyTabBarItemOptions:self];
110
+-(void)applyTopTabsOptions {
111
+	[self.navigationOptions.topTab applyOn:self];
117 112
 }
118 113
 
119 114
 /**

+ 11
- 0
lib/ios/RNNRootViewProtocol.h View File

@@ -0,0 +1,11 @@
1
+#import "RNNNavigationOptions.h"
2
+
3
+@protocol RNNRootViewProtocol <NSObject, UINavigationControllerDelegate>
4
+
5
+@required
6
+
7
+- (BOOL)isCustomTransitioned;
8
+
9
+@end
10
+
11
+

+ 8
- 0
lib/ios/RNNSegmentedControl.h View File

@@ -0,0 +1,8 @@
1
+#import <Foundation/Foundation.h>
2
+#import "HMSegmentedControl.h"
3
+
4
+@interface RNNSegmentedControl : HMSegmentedControl
5
+
6
+- (void)setTitle:(NSString*)title atIndex:(NSUInteger)index;
7
+
8
+@end

+ 11
- 0
lib/ios/RNNSegmentedControl.m View File

@@ -0,0 +1,11 @@
1
+#import "RNNSegmentedControl.h"
2
+
3
+@implementation RNNSegmentedControl
4
+
5
+- (void)setTitle:(NSString*)title atIndex:(NSUInteger)index {
6
+	NSMutableArray* mutableTitles = [[NSMutableArray alloc] initWithArray:self.sectionTitles];
7
+	[mutableTitles setObject:title atIndexedSubscript:index];
8
+	[self setSectionTitles:mutableTitles];
9
+}
10
+
11
+@end

+ 2
- 1
lib/ios/RNNSideMenuChildVC.h View File

@@ -7,6 +7,7 @@
7 7
 //
8 8
 
9 9
 #import <UIKit/UIKit.h>
10
+#import "RNNRootViewProtocol.h"
10 11
 
11 12
 typedef NS_ENUM(NSInteger, RNNSideMenuChildType) {
12 13
 	RNNSideMenuChildTypeCenter,
@@ -15,7 +16,7 @@ typedef NS_ENUM(NSInteger, RNNSideMenuChildType) {
15 16
 };
16 17
 
17 18
 
18
-@interface RNNSideMenuChildVC : UIViewController
19
+@interface RNNSideMenuChildVC : UIViewController <RNNRootViewProtocol>
19 20
 
20 21
 @property (readonly) RNNSideMenuChildType type;
21 22
 @property (readonly) UIViewController *child;

+ 4
- 0
lib/ios/RNNSideMenuChildVC.m View File

@@ -33,4 +33,8 @@
33 33
 	return self;
34 34
 }
35 35
 
36
+- (BOOL)isCustomTransitioned {
37
+	return NO;
38
+}
39
+
36 40
 @end

+ 2
- 1
lib/ios/RNNSideMenuController.h View File

@@ -9,8 +9,9 @@
9 9
 #import <UIKit/UIKit.h>
10 10
 #import "RNNSideMenuChildVC.h"
11 11
 #import "MMDrawerController.h"
12
+#import "RNNRootViewProtocol.h"
12 13
 
13
-@interface RNNSideMenuController : UIViewController
14
+@interface RNNSideMenuController : UIViewController <RNNRootViewProtocol>
14 15
 
15 16
 @property (readonly) RNNSideMenuChildVC *center;
16 17
 @property (readonly) RNNSideMenuChildVC *left;

+ 4
- 0
lib/ios/RNNSideMenuController.m View File

@@ -72,4 +72,8 @@
72 72
 	}
73 73
 }
74 74
 
75
+- (BOOL)isCustomTransitioned {
76
+	return NO;
77
+}
78
+
75 79
 @end

+ 2
- 8
lib/ios/RNNSideMenuOptions.h View File

@@ -1,14 +1,8 @@
1
-#import <Foundation/Foundation.h>
1
+#import "RNNOptions.h"
2 2
 
3
-@interface RNNSideMenuOptions : NSObject
3
+@interface RNNSideMenuOptions : RNNOptions
4 4
 
5 5
 @property (nonatomic, strong) NSNumber* leftSideVisible;
6 6
 @property (nonatomic, strong) NSNumber* rightSideVisible;
7 7
 
8
--(instancetype)init;
9
--(instancetype)initWithDict:(NSDictionary *)sideMenuOptions;
10
--(void)mergeWith:(NSDictionary*)otherOptions;
11
-
12
--(void)resetOptions;
13
-
14 8
 @end

+ 22
- 10
lib/ios/RNNSideMenuOptions.m View File

@@ -1,17 +1,29 @@
1 1
 #import "RNNSideMenuOptions.h"
2
+#import "RNNSideMenuController.h"
2 3
 
3 4
 @implementation RNNSideMenuOptions
4 5
 
5
--(instancetype)init {
6
-	return [self initWithDict:@{}];
7
-}
8
-
9
--(instancetype)initWithDict:(NSDictionary *)sideMenuOptions {
10
-	self = [super init];
11
-	
12
-	[self mergeWith:sideMenuOptions];
13
-	
14
-	return self;
6
+- (void)applyOn:(UIViewController *)viewController {
7
+	RNNSideMenuController* sideMenuController = (RNNSideMenuController*)UIApplication.sharedApplication.delegate.window.rootViewController;
8
+	if ([sideMenuController isKindOfClass:[RNNSideMenuController class]]) {
9
+		if (self.leftSideVisible) {
10
+			if (self.leftSideVisible.boolValue) {
11
+				[sideMenuController showSideMenu:MMDrawerSideLeft animated:YES];
12
+			} else {
13
+				[sideMenuController hideSideMenu:MMDrawerSideLeft animated:YES];
14
+			}
15
+		}
16
+		
17
+		if (self.rightSideVisible) {
18
+			if (self.rightSideVisible.boolValue) {
19
+				[sideMenuController showSideMenu:MMDrawerSideRight animated:YES];
20
+			} else {
21
+				[sideMenuController hideSideMenu:MMDrawerSideRight animated:YES];
22
+			}
23
+		}
24
+		
25
+		[self resetOptions];
26
+	}
15 27
 }
16 28
 
17 29
 -(void)mergeWith:(NSDictionary *)otherOptions {

+ 2
- 1
lib/ios/RNNTabBarController.h View File

@@ -1,7 +1,8 @@
1 1
 
2 2
 #import <UIKit/UIKit.h>
3
+#import "RNNRootViewProtocol.h"
3 4
 
4
-@interface RNNTabBarController : UITabBarController
5
+@interface RNNTabBarController : UITabBarController <RNNRootViewProtocol>
5 6
 
6 7
 - (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;
7 8
 - (void)setSelectedIndexByContainerID:(NSString *)containerID;

+ 4
- 0
lib/ios/RNNTabBarController.m View File

@@ -33,4 +33,8 @@
33 33
 	}
34 34
 }
35 35
 
36
+- (BOOL)isCustomTransitioned {
37
+	return NO;
38
+}
39
+
36 40
 @end

+ 0
- 32
lib/ios/RNNTabBarOptions.m View File

@@ -1,32 +0,0 @@
1
-#import "RNNTabBarOptions.h"
2
-
3
-@implementation RNNTabBarOptions
4
-
5
--(instancetype)init {
6
-	return [self initWithDict:@{}];
7
-}
8
-
9
--(instancetype)initWithDict:(NSDictionary *)tabBarOptions {
10
-	self = [super init];
11
-	
12
-	self.hidden = [tabBarOptions valueForKey:@"hidden"];
13
-	self.animateHide = [tabBarOptions valueForKey:@"animateHide"];
14
-	self.currentTabIndex = [tabBarOptions valueForKey:@"currentTabIndex"];
15
-	self.testID = [tabBarOptions valueForKey:@"testID"];
16
-	self.currentTabId = [tabBarOptions valueForKey:@"currentTabId"];
17
-	self.drawUnder = [tabBarOptions valueForKey:@"drawUnder"];
18
-	
19
-	return self;
20
-}
21
-
22
-- (void)resetOptions {
23
-	self.currentTabId = nil;
24
-	self.currentTabIndex = nil;
25
-}
26
-
27
--(void)mergeWith:(NSDictionary *)otherOptions {
28
-	for (id key in otherOptions) {
29
-		[self setValue:[otherOptions objectForKey:key] forKey:key];
30
-	}
31
-}
32
-@end

+ 0
- 32
lib/ios/RNNTabItemOptions.m View File

@@ -1,32 +0,0 @@
1
-#import "RNNTabItemOptions.h"
2
-
3
-@implementation RNNTabItemOptions
4
-
5
--(instancetype)initWithDict:(NSDictionary *)tabItemDict {
6
-	self = [super init];
7
-	
8
-	self.title = tabItemDict[@"title"];
9
-	self.tag = [tabItemDict[@"tag"] integerValue];
10
-	self.badge = tabItemDict[@"badge"];
11
-	self.testID = tabItemDict[@"testID"];
12
-	self.visible = tabItemDict[@"visible"];
13
-	self.icon = tabItemDict[@"icon"];
14
-	
15
-	return self;
16
-}
17
-
18
--(void)mergeWith:(NSDictionary *)otherOptions {
19
-	for (id key in otherOptions) {
20
-		[self setValue:[otherOptions objectForKey:key] forKey:key];
21
-	}
22
-}
23
-
24
--(void)resetOptions {
25
-	self.title = nil;
26
-	self.badge = nil;
27
-	self.visible = nil;
28
-	self.icon = nil;
29
-	self.testID = nil;
30
-}
31
-
32
-@end

+ 4
- 8
lib/ios/RNNTopBarOptions.h View File

@@ -1,9 +1,9 @@
1
-#import <Foundation/Foundation.h>
1
+#import "RNNOptions.h"
2 2
 
3
-extern const NSInteger BLUR_TOPBAR_TAG;
4
-
5
-@interface RNNTopBarOptions : NSObject
3
+@interface RNNTopBarOptions : RNNOptions
6 4
 
5
+@property (nonatomic, strong) NSArray* leftButtons;
6
+@property (nonatomic, strong) NSArray* rightButtons;
7 7
 @property (nonatomic, strong) NSNumber* backgroundColor;
8 8
 @property (nonatomic, strong) NSNumber* textColor;
9 9
 @property (nonatomic, strong) NSString* title;
@@ -21,8 +21,4 @@ extern const NSInteger BLUR_TOPBAR_TAG;
21 21
 @property (nonatomic, strong) NSNumber* largeTitle;
22 22
 @property (nonatomic, strong) NSString* testID;
23 23
 
24
--(instancetype)init;
25
--(instancetype)initWithDict:(NSDictionary *)topBarOptions;
26
--(void)mergeWith:(NSDictionary*)otherOptions;
27
-
28 24
 @end

+ 164
- 28
lib/ios/RNNTopBarOptions.m View File

@@ -1,39 +1,175 @@
1 1
 #import "RNNTopBarOptions.h"
2
+#import "RNNNavigationButtons.h"
2 3
 
3
-@implementation RNNTopBarOptions
4
+extern const NSInteger BLUR_TOPBAR_TAG;
4 5
 
5
--(instancetype)init {
6
-	return [self initWithDict:@{}];
7
-}
6
+@interface RNNTopBarOptions ()
7
+
8
+@property (nonatomic, strong) NSMutableDictionary* originalTopBarImages;
9
+@property (nonatomic, strong) RNNNavigationButtons* navigationButtons;
10
+
11
+@end
8 12
 
9
--(instancetype)initWithDict:(NSDictionary *)topBarOptions {
10
-	self = [super init];
11
-	
12
-	self.title = [topBarOptions valueForKey:@"title"];
13
-	self.backgroundColor = [topBarOptions valueForKey:@"backgroundColor"];
14
-	self.textColor = [topBarOptions valueForKey:@"textColor"];
15
-	self.textFontFamily = [topBarOptions valueForKey:@"textFontFamily"];
16
-	self.textFontSize = [topBarOptions valueForKey:@"textFontSize"];
17
-	self.hidden = [topBarOptions valueForKey:@"hidden"];
18
-	self.hideOnScroll = [topBarOptions valueForKey:@"hideOnScroll"];
19
-	self.buttonColor = [topBarOptions valueForKey:@"buttonColor"];
20
-	self.blur = [topBarOptions valueForKey:@"blur"];
21
-	self.translucent = [topBarOptions valueForKey:@"translucent"];
22
-	self.transparent = [topBarOptions valueForKey:@"transparent"];
23
-	self.noBorder = [topBarOptions valueForKey:@"noBorder"];
24
-	self.animateHide = [topBarOptions valueForKey:@"animateHide"];
25
-	self.largeTitle = [topBarOptions valueForKey:@"largeTitle"];
26
-	self.testID = [topBarOptions valueForKey:@"testID"];
27
-	self.drawUnder = [topBarOptions valueForKey:@"drawUnder"];
28
-	
29
-	return self;
13
+@implementation RNNTopBarOptions
14
+
15
+- (void)applyOn:(UIViewController*)viewController {
16
+	if (self.backgroundColor) {
17
+		UIColor* backgroundColor = [RCTConvert UIColor:self.backgroundColor];
18
+		viewController.navigationController.navigationBar.barTintColor = backgroundColor;
19
+	} else {
20
+		viewController.navigationController.navigationBar.barTintColor = nil;
21
+	}
22
+	
23
+	if (self.title) {
24
+		viewController.navigationItem.title = self.title;
25
+	}
26
+	
27
+	if (@available(iOS 11.0, *)) {
28
+		if (self.largeTitle){
29
+			if ([self.largeTitle boolValue]) {
30
+				viewController.navigationController.navigationBar.prefersLargeTitles = YES;
31
+				viewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlways;
32
+			} else {
33
+				viewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
34
+			}
35
+		} else {
36
+			viewController.navigationController.navigationBar.prefersLargeTitles = NO;
37
+			viewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
38
+		}
39
+	}
40
+	
41
+	
42
+	if (self.textFontFamily || self.textFontSize || self.textColor) {
43
+		NSMutableDictionary* navigationBarTitleTextAttributes = [NSMutableDictionary new];
44
+		if (self.textColor) {
45
+			navigationBarTitleTextAttributes[NSForegroundColorAttributeName] = [RCTConvert UIColor:[self valueForKey:@"textColor"]];
46
+		}
47
+		if (self.textFontFamily){
48
+			if(self.textFontSize) {
49
+				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.textFontFamily size:[self.textFontSize floatValue]];
50
+			} else {
51
+				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.textFontFamily size:20];
52
+			}
53
+		} else if (self.textFontSize) {
54
+			navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont systemFontOfSize:[self.textFontSize floatValue]];
55
+		}
56
+		viewController.navigationController.navigationBar.titleTextAttributes = navigationBarTitleTextAttributes;
57
+		if (@available(iOS 11.0, *)){
58
+			viewController.navigationController.navigationBar.largeTitleTextAttributes = navigationBarTitleTextAttributes;
59
+		}
60
+		
61
+	}
62
+	
63
+	
64
+	if (self.hidden){
65
+		[viewController.navigationController setNavigationBarHidden:[self.hidden boolValue] animated:[self.animateHide boolValue]];
66
+	}
67
+	
68
+	if (self.hideOnScroll) {
69
+		viewController.navigationController.hidesBarsOnSwipe = [self.hideOnScroll boolValue];
70
+	}
71
+	
72
+	if (self.buttonColor) {
73
+		UIColor* buttonColor = [RCTConvert UIColor:self.buttonColor];
74
+		viewController.navigationController.navigationBar.tintColor = buttonColor;
75
+	} else {
76
+		viewController.navigationController.navigationBar.tintColor = nil;
77
+	}
78
+	
79
+	if ([self.blur boolValue]) {
80
+		if (![viewController.navigationController.navigationBar viewWithTag:BLUR_TOPBAR_TAG]) {
81
+			
82
+			[viewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
83
+			viewController.navigationController.navigationBar.shadowImage = [UIImage new];
84
+			UIVisualEffectView *blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
85
+			CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
86
+			blur.frame = CGRectMake(0, -1 * statusBarFrame.size.height, viewController.navigationController.navigationBar.frame.size.width, viewController.navigationController.navigationBar.frame.size.height + statusBarFrame.size.height);
87
+			blur.userInteractionEnabled = NO;
88
+			blur.tag = BLUR_TOPBAR_TAG;
89
+			[viewController.navigationController.navigationBar insertSubview:blur atIndex:0];
90
+			[viewController.navigationController.navigationBar sendSubviewToBack:blur];
91
+		}
92
+	} else {
93
+		UIView *blur = [viewController.navigationController.navigationBar viewWithTag:BLUR_TOPBAR_TAG];
94
+		if (blur) {
95
+			[viewController.navigationController.navigationBar setBackgroundImage: nil forBarMetrics:UIBarMetricsDefault];
96
+			viewController.navigationController.navigationBar.shadowImage = nil;
97
+			[blur removeFromSuperview];
98
+		}
99
+	}
100
+	
101
+	void (^disableTopBarTransparent)() = ^void(){
102
+		UIView *transparentView = [viewController.navigationController.navigationBar viewWithTag:TOP_BAR_TRANSPARENT_TAG];
103
+		if (transparentView){
104
+			[transparentView removeFromSuperview];
105
+			[viewController.navigationController.navigationBar setBackgroundImage:self.originalTopBarImages[@"backgroundImage"] forBarMetrics:UIBarMetricsDefault];
106
+			viewController.navigationController.navigationBar.shadowImage = self.originalTopBarImages[@"shadowImage"];
107
+			self.originalTopBarImages = nil;
108
+		}
109
+	};
110
+	
111
+	if (self.transparent) {
112
+		if ([self.transparent boolValue]) {
113
+			if (![viewController.navigationController.navigationBar viewWithTag:TOP_BAR_TRANSPARENT_TAG]){
114
+				[self storeOriginalTopBarImages:viewController];
115
+				[viewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
116
+				viewController.navigationController.navigationBar.shadowImage = [UIImage new];
117
+				UIView *transparentView = [[UIView alloc] initWithFrame:CGRectZero];
118
+				transparentView.tag = TOP_BAR_TRANSPARENT_TAG;
119
+				[viewController.navigationController.navigationBar insertSubview:transparentView atIndex:0];
120
+			}
121
+		} else {
122
+			disableTopBarTransparent();
123
+		}
124
+	} else {
125
+		disableTopBarTransparent();
126
+	}
127
+	
128
+	if (self.translucent) {
129
+		viewController.navigationController.navigationBar.translucent = [self.translucent boolValue];
130
+	}
131
+	
132
+	if (self.drawUnder) {
133
+		if ([self.drawUnder boolValue]) {
134
+			viewController.edgesForExtendedLayout |= UIRectEdgeTop;
135
+		} else {
136
+			viewController.edgesForExtendedLayout &= ~UIRectEdgeTop;
137
+		}
138
+	}
139
+	
140
+	if (self.noBorder) {
141
+		if ([self.noBorder boolValue]) {
142
+			viewController.navigationController.navigationBar
143
+			.shadowImage = [[UIImage alloc] init];
144
+		} else {
145
+			viewController.navigationController.navigationBar
146
+			.shadowImage = nil;
147
+		}
148
+	}
149
+	
150
+	if (self.testID) {
151
+		viewController.navigationController.navigationBar.accessibilityIdentifier = self.testID;
152
+	}
153
+	
154
+	if (self.rightButtons || self.leftButtons) {
155
+		_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:(RNNRootViewController*)viewController];
156
+		[_navigationButtons applyLeftButtons:self.leftButtons rightButtons:self.rightButtons];
157
+	}
30 158
 }
31 159
 
32
--(void)mergeWith:(NSDictionary *)otherOptions {
33
-	for (id key in otherOptions) {
34
-		[self setValue:[otherOptions objectForKey:key] forKey:key];
160
+-(void)storeOriginalTopBarImages:(UIViewController*)viewController {
161
+	NSMutableDictionary *originalTopBarImages = [@{} mutableCopy];
162
+	UIImage *bgImage = [viewController.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault];
163
+	if (bgImage != nil) {
164
+		originalTopBarImages[@"backgroundImage"] = bgImage;
35 165
 	}
166
+	UIImage *shadowImage = viewController.navigationController.navigationBar.shadowImage;
167
+	if (shadowImage != nil) {
168
+		originalTopBarImages[@"shadowImage"] = shadowImage;
169
+	}
170
+	self.originalTopBarImages = originalTopBarImages;
36 171
 }
172
+
37 173
 @end
38 174
 
39 175
 

+ 9
- 0
lib/ios/RNNTopTabOptions.h View File

@@ -0,0 +1,9 @@
1
+#import "RNNOptions.h"
2
+
3
+@interface RNNTopTabOptions : RNNOptions
4
+
5
+@property (nonatomic, strong) NSString* title;
6
+@property (nonatomic, strong) NSString* titleFontFamily;
7
+
8
+@end
9
+

+ 18
- 0
lib/ios/RNNTopTabOptions.m View File

@@ -0,0 +1,18 @@
1
+#import "RNNTopTabOptions.h"
2
+#import "RNNRootViewController.h"
3
+
4
+@implementation RNNTopTabOptions
5
+
6
+- (void)applyOn:(RNNRootViewController*)viewController {
7
+	if (self.title) {
8
+		[viewController.topTabsViewController viewController:viewController changedTitle:self.title];
9
+	}
10
+}
11
+
12
+-(void)mergeWith:(NSDictionary *)otherOptions {
13
+	for (id key in otherOptions) {
14
+		[self setValue:[otherOptions objectForKey:key] forKey:key];
15
+	}
16
+}
17
+@end
18
+

+ 7
- 0
lib/ios/RNNTopTabsOptions.h View File

@@ -0,0 +1,7 @@
1
+#import "RNNOptions.h"
2
+
3
+@interface RNNTopTabsOptions : RNNOptions
4
+
5
+@property (nonatomic, strong) NSNumber* backgroundColor;
6
+
7
+@end

+ 10
- 0
lib/ios/RNNTopTabsOptions.m View File

@@ -0,0 +1,10 @@
1
+#import "RNNTopTabsOptions.h"
2
+#import "RNNTopTabsViewController.h"
3
+
4
+@implementation RNNTopTabsOptions
5
+
6
+- (void)applyOn:(RNNTopTabsViewController *)viewController {
7
+
8
+}
9
+
10
+@end

+ 14
- 0
lib/ios/RNNTopTabsViewController.h View File

@@ -0,0 +1,14 @@
1
+#import <Foundation/Foundation.h>
2
+#import <UIKit/UIKit.h>
3
+#import <React/RCTUIManager.h>
4
+#import "RNNRootViewProtocol.h"
5
+
6
+@interface RNNTopTabsViewController : UIViewController <RNNRootViewProtocol>
7
+
8
+@property (nonatomic, retain) UIView* contentView;
9
+
10
+- (void)setViewControllers:(NSArray*)viewControllers;
11
+- (void)viewController:(UIViewController*)vc changedTitle:(NSString*)title;
12
+- (instancetype)init;
13
+
14
+@end

+ 79
- 0
lib/ios/RNNTopTabsViewController.m View File

@@ -0,0 +1,79 @@
1
+#import "RNNTopTabsViewController.h"
2
+#import "RNNSegmentedControl.h"
3
+#import "RNNRootViewController.h"
4
+#import "ReactNativeNavigation.h"
5
+
6
+@interface RNNTopTabsViewController () {
7
+	NSArray* _viewControllers;
8
+	UIViewController* _currentViewController;
9
+	RNNSegmentedControl* _segmentedControl;
10
+}
11
+
12
+@end
13
+
14
+@implementation RNNTopTabsViewController
15
+
16
+- (instancetype)init {
17
+	self = [super init];
18
+	
19
+	[self.view setBackgroundColor:[UIColor whiteColor]];
20
+	self.edgesForExtendedLayout = UIRectEdgeNone;
21
+	
22
+	[self createTabBar];
23
+	[self createContentView];
24
+	
25
+	return self;
26
+}
27
+
28
+- (void)createTabBar {
29
+	_segmentedControl = [[RNNSegmentedControl alloc] initWithSectionTitles:@[@"", @"", @""]];
30
+	_segmentedControl.frame = CGRectMake(0, 0, self.view.bounds.size.width, 50);
31
+	_segmentedControl.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationNone;
32
+	_segmentedControl.selectionStyle = HMSegmentedControlSelectionStyleBox;
33
+	_segmentedControl.selectedSegmentIndex = HMSegmentedControlNoSegment;
34
+	
35
+	[_segmentedControl addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged];
36
+	[self.view addSubview:_segmentedControl];
37
+}
38
+
39
+- (void)segmentedControlChangedValue:(HMSegmentedControl*)segmentedControl {
40
+	[self setSelectedViewControllerIndex:segmentedControl.selectedSegmentIndex];
41
+}
42
+
43
+- (void)createContentView {
44
+	_contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 50, self.view.bounds.size.width, self.view.bounds.size.height - 50)];
45
+	_contentView.backgroundColor = [UIColor grayColor];
46
+	[self.view addSubview:_contentView];
47
+}
48
+
49
+- (void)setSelectedViewControllerIndex:(NSUInteger)index {
50
+	UIViewController *toVC = _viewControllers[index];
51
+	[_contentView addSubview:toVC.view];
52
+	[_currentViewController.view removeFromSuperview];
53
+	_currentViewController = toVC;
54
+}
55
+
56
+- (void)setViewControllers:(NSArray *)viewControllers {
57
+	_viewControllers = viewControllers;
58
+	for (RNNRootViewController* childVc in viewControllers) {
59
+		[childVc.view setFrame:_contentView.bounds];
60
+		[childVc applyTopTabsOptions];
61
+	}
62
+	
63
+	[self setSelectedViewControllerIndex:0];
64
+}
65
+
66
+- (void)viewController:(UIViewController*)vc changedTitle:(NSString*)title {
67
+	NSUInteger vcIndex = [_viewControllers indexOfObject:vc];
68
+	[_segmentedControl setTitle:title atIndex:vcIndex];
69
+}
70
+
71
+- (void)viewDidLoad {
72
+    [super viewDidLoad];
73
+}
74
+
75
+- (BOOL)isCustomTransitioned {
76
+	return NO;
77
+}
78
+
79
+@end

+ 0
- 2
lib/ios/ReactNativeNavigation.h View File

@@ -1,6 +1,4 @@
1
-
2 1
 #import <Foundation/Foundation.h>
3
-#import <UIKit/UIKit.h>
4 2
 
5 3
 @interface ReactNativeNavigation : NSObject
6 4
 

+ 8
- 80
lib/ios/ReactNativeNavigation.m View File

@@ -1,28 +1,19 @@
1
-
2 1
 #import "ReactNativeNavigation.h"
3 2
 
3
+#import <UIKit/UIKit.h>
4 4
 #import <React/RCTBridge.h>
5 5
 #import <React/RCTUIManager.h>
6 6
 
7
-#import "RNNEventEmitter.h"
7
+#import "RNNBridgeManager.h"
8 8
 #import "RNNSplashScreen.h"
9
-#import "RNNBridgeModule.h"
10
-#import "RNNRootViewCreator.h"
11
-#import "RNNReactRootViewCreator.h"
12 9
 
13
-@interface ReactNativeNavigation() <RCTBridgeDelegate>
10
+@interface ReactNativeNavigation()
11
+
12
+@property (nonatomic, strong) RNNBridgeManager *bridgeManager;
14 13
 
15 14
 @end
16 15
 
17
-@implementation ReactNativeNavigation {
18
-	NSURL* _jsCodeLocation;
19
-	NSDictionary* _launchOptions;
20
-	RCTBridge* _bridge;
21
-	
22
-	RNNStore* _store;
23
-	
24
-	RNNCommandsHandler* _commandsHandler;
25
-}
16
+@implementation ReactNativeNavigation
26 17
 
27 18
 # pragma mark - public API
28 19
 
@@ -32,7 +23,7 @@
32 23
 
33 24
 # pragma mark - instance
34 25
 
35
-+(instancetype) sharedInstance {
26
++ (instancetype) sharedInstance {
36 27
 	static ReactNativeNavigation *instance = nil;
37 28
 	static dispatch_once_t onceToken = 0;
38 29
 	dispatch_once(&onceToken,^{
@@ -45,71 +36,8 @@
45 36
 }
46 37
 
47 38
 -(void)bootstrap:(NSURL *)jsCodeLocation launchOptions:(NSDictionary *)launchOptions {
48
-	_jsCodeLocation = jsCodeLocation;
49
-	_launchOptions = launchOptions;
50
-	_store = [RNNStore new];
51
-	
39
+	self.bridgeManager = [[RNNBridgeManager alloc] initWithJsCodeLocation:jsCodeLocation launchOptions:launchOptions];
52 40
 	[RNNSplashScreen show];
53
-	
54
-	[self registerForJsEvents];
55
-	
56
-	[self createBridgeLoadJsAndThenInitDependencyGraph];
57
-}
58
-
59
-# pragma mark - RCTBridgeDelegate
60
-
61
--(NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
62
-	return _jsCodeLocation;
63
-}
64
-
65
-/**
66
- * here we initialize all of our dependency graph
67
- */
68
--(NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
69
-	RNNEventEmitter *eventEmitter = [[RNNEventEmitter alloc] init];
70
-	
71
-	id<RNNRootViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge];
72
-	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator store:_store eventEmitter:eventEmitter];
73
-	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store controllerFactory:controllerFactory andBridge:bridge];
74
-	RNNBridgeModule *bridgeModule = [[RNNBridgeModule alloc] initWithCommandsHandler:_commandsHandler];
75
-	
76
-	return @[bridgeModule,eventEmitter];
77
-}
78
-
79
-# pragma mark - js events
80
-
81
--(void)onJavaScriptWillLoad {
82
-	[_store clean];
83
-}
84
-
85
--(void)onJavaScriptLoaded {
86
-	[_store setReadyToReceiveCommands:true];
87
-	[[_bridge moduleForClass:[RNNEventEmitter class]] sendOnAppLaunched];
88
-}
89
-
90
--(void)onBridgeWillReload {
91
-	UIApplication.sharedApplication.delegate.window.rootViewController =  nil;
92
-}
93
-
94
-# pragma mark - private
95
-
96
--(void)createBridgeLoadJsAndThenInitDependencyGraph {
97
-	_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:_launchOptions];
98
-}
99
-
100
--(void)registerForJsEvents {
101
-	[[NSNotificationCenter defaultCenter] addObserver:self
102
-											 selector:@selector(onJavaScriptLoaded)
103
-												 name:RCTJavaScriptDidLoadNotification
104
-											   object:nil];
105
-	[[NSNotificationCenter defaultCenter] addObserver:self
106
-											 selector:@selector(onJavaScriptWillLoad)
107
-												 name:RCTJavaScriptWillStartLoadingNotification
108
-											   object:nil];
109
-	[[NSNotificationCenter defaultCenter] addObserver:self
110
-											 selector:@selector(onBridgeWillReload)
111
-												 name:RCTBridgeWillReloadNotification
112
-											   object:nil];
113 41
 }
114 42
 
115 43
 @end

+ 98
- 20
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj View File

@@ -57,10 +57,26 @@
57 57
 		268692831E5054F800E2C612 /* RNNStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 268692811E5054F800E2C612 /* RNNStore.m */; };
58 58
 		26916C981E4B9E7700D13680 /* RNNReactRootViewCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = 26916C961E4B9E7700D13680 /* RNNReactRootViewCreator.h */; };
59 59
 		26916C991E4B9E7700D13680 /* RNNReactRootViewCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = 26916C971E4B9E7700D13680 /* RNNReactRootViewCreator.m */; };
60
+		2DCD9195200014A900EDC75D /* RNNBridgeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DCD9193200014A900EDC75D /* RNNBridgeManager.h */; };
61
+		2DCD9196200014A900EDC75D /* RNNBridgeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DCD9194200014A900EDC75D /* RNNBridgeManager.m */; };
62
+		504AFE641FFE53070076E904 /* RNNOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 504AFE621FFE53070076E904 /* RNNOptions.h */; };
63
+		504AFE651FFE53070076E904 /* RNNOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 504AFE631FFE53070076E904 /* RNNOptions.m */; };
64
+		504AFE741FFFF0540076E904 /* RNNTopTabsOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 504AFE721FFFF0540076E904 /* RNNTopTabsOptions.h */; };
65
+		504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */; };
66
+		504AFE761FFFF1E00076E904 /* RNNNavigationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */; };
67
+		504AFE771FFFF1E20076E904 /* RNNTopBarOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */; };
68
+		507F43C51FF4F17C00D9425B /* RNNTopTabsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 507F43C31FF4F17C00D9425B /* RNNTopTabsViewController.h */; };
69
+		507F43C61FF4F17C00D9425B /* RNNTopTabsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 507F43C41FF4F17C00D9425B /* RNNTopTabsViewController.m */; };
70
+		507F43C91FF4F9CC00D9425B /* RNNTopTabOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 507F43C71FF4F9CC00D9425B /* RNNTopTabOptions.h */; };
71
+		507F43CA1FF4F9CC00D9425B /* RNNTopTabOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 507F43C81FF4F9CC00D9425B /* RNNTopTabOptions.m */; };
72
+		507F43F41FF4FCFE00D9425B /* HMSegmentedControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 507F43F21FF4FCFE00D9425B /* HMSegmentedControl.h */; };
73
+		507F43F51FF4FCFE00D9425B /* HMSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 507F43F31FF4FCFE00D9425B /* HMSegmentedControl.m */; };
74
+		507F43F81FF525B500D9425B /* RNNSegmentedControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 507F43F61FF525B500D9425B /* RNNSegmentedControl.h */; };
75
+		507F43F91FF525B500D9425B /* RNNSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 507F43F71FF525B500D9425B /* RNNSegmentedControl.m */; };
76
+		507F44201FFA8A8800D9425B /* RNNRootViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 507F441F1FFA8A8800D9425B /* RNNRootViewProtocol.h */; };
60 77
 		50CB3B691FDE911400AA153B /* RNNSideMenuOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50CB3B671FDE911400AA153B /* RNNSideMenuOptions.h */; };
61 78
 		50CB3B6A1FDE911400AA153B /* RNNSideMenuOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50CB3B681FDE911400AA153B /* RNNSideMenuOptions.m */; };
62
-		50EB93411FE14A3E00BD8EEE /* RNNTabItemOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50EB933F1FE14A3E00BD8EEE /* RNNTabItemOptions.h */; };
63
-		50EB93421FE14A3E00BD8EEE /* RNNTabItemOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50EB93401FE14A3E00BD8EEE /* RNNTabItemOptions.m */; };
79
+		50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50EB93401FE14A3E00BD8EEE /* RNNBottomTabOptions.m */; };
64 80
 		50F5DFC11F407A8C001A00BC /* RNNTabBarController.h in Headers */ = {isa = PBXBuildFile; fileRef = 50F5DFBF1F407A8C001A00BC /* RNNTabBarController.h */; };
65 81
 		50F5DFC21F407A8C001A00BC /* RNNTabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F5DFC01F407A8C001A00BC /* RNNTabBarController.m */; };
66 82
 		50F5DFC51F407AA0001A00BC /* RNNNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 50F5DFC31F407AA0001A00BC /* RNNNavigationController.h */; };
@@ -102,7 +118,7 @@
102 118
 		7BEF0D1C1E43771B003E96B0 /* RNNLayoutNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEF0D1A1E43771B003E96B0 /* RNNLayoutNode.h */; };
103 119
 		7BEF0D1D1E43771B003E96B0 /* RNNLayoutNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BEF0D1B1E43771B003E96B0 /* RNNLayoutNode.m */; };
104 120
 		A7626BFD1FC2FB2C00492FB8 /* RNNTopBarOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */; };
105
-		A7626C011FC5796200492FB8 /* RNNTabBarOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A7626C001FC5796200492FB8 /* RNNTabBarOptions.m */; };
121
+		A7626C011FC5796200492FB8 /* RNNBottomTabsOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A7626C001FC5796200492FB8 /* RNNBottomTabsOptions.m */; };
106 122
 		E8367B801F7A8A4700675C05 /* VICMAImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = E8367B7E1F7A8A4700675C05 /* VICMAImageView.h */; };
107 123
 		E8367B811F7A8A4700675C05 /* VICMAImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = E8367B7F1F7A8A4700675C05 /* VICMAImageView.m */; };
108 124
 		E83BAD681F2734B500A9F3DD /* RNNNavigationOptionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E83BAD671F2734B500A9F3DD /* RNNNavigationOptionsTest.m */; };
@@ -207,10 +223,25 @@
207 223
 		26916C941E4B9CCC00D13680 /* RNNRootViewCreator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNRootViewCreator.h; sourceTree = "<group>"; };
208 224
 		26916C961E4B9E7700D13680 /* RNNReactRootViewCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNReactRootViewCreator.h; sourceTree = "<group>"; };
209 225
 		26916C971E4B9E7700D13680 /* RNNReactRootViewCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNReactRootViewCreator.m; sourceTree = "<group>"; };
226
+		2DCD9193200014A900EDC75D /* RNNBridgeManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBridgeManager.h; sourceTree = "<group>"; };
227
+		2DCD9194200014A900EDC75D /* RNNBridgeManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBridgeManager.m; sourceTree = "<group>"; };
228
+		504AFE621FFE53070076E904 /* RNNOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNOptions.h; sourceTree = "<group>"; };
229
+		504AFE631FFE53070076E904 /* RNNOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNOptions.m; sourceTree = "<group>"; };
230
+		504AFE721FFFF0540076E904 /* RNNTopTabsOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopTabsOptions.h; sourceTree = "<group>"; };
231
+		504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopTabsOptions.m; sourceTree = "<group>"; };
232
+		507F43C31FF4F17C00D9425B /* RNNTopTabsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopTabsViewController.h; sourceTree = "<group>"; };
233
+		507F43C41FF4F17C00D9425B /* RNNTopTabsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopTabsViewController.m; sourceTree = "<group>"; };
234
+		507F43C71FF4F9CC00D9425B /* RNNTopTabOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopTabOptions.h; sourceTree = "<group>"; };
235
+		507F43C81FF4F9CC00D9425B /* RNNTopTabOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopTabOptions.m; sourceTree = "<group>"; };
236
+		507F43F21FF4FCFE00D9425B /* HMSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMSegmentedControl.h; sourceTree = "<group>"; };
237
+		507F43F31FF4FCFE00D9425B /* HMSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMSegmentedControl.m; sourceTree = "<group>"; };
238
+		507F43F61FF525B500D9425B /* RNNSegmentedControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNSegmentedControl.h; sourceTree = "<group>"; };
239
+		507F43F71FF525B500D9425B /* RNNSegmentedControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSegmentedControl.m; sourceTree = "<group>"; };
240
+		507F441F1FFA8A8800D9425B /* RNNRootViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNRootViewProtocol.h; sourceTree = "<group>"; };
210 241
 		50CB3B671FDE911400AA153B /* RNNSideMenuOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNSideMenuOptions.h; sourceTree = "<group>"; };
211 242
 		50CB3B681FDE911400AA153B /* RNNSideMenuOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSideMenuOptions.m; sourceTree = "<group>"; };
212
-		50EB933F1FE14A3E00BD8EEE /* RNNTabItemOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTabItemOptions.h; sourceTree = "<group>"; };
213
-		50EB93401FE14A3E00BD8EEE /* RNNTabItemOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTabItemOptions.m; sourceTree = "<group>"; };
243
+		50EB933F1FE14A3E00BD8EEE /* RNNBottomTabOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBottomTabOptions.h; sourceTree = "<group>"; };
244
+		50EB93401FE14A3E00BD8EEE /* RNNBottomTabOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBottomTabOptions.m; sourceTree = "<group>"; };
214 245
 		50F5DFBF1F407A8C001A00BC /* RNNTabBarController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTabBarController.h; sourceTree = "<group>"; };
215 246
 		50F5DFC01F407A8C001A00BC /* RNNTabBarController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTabBarController.m; sourceTree = "<group>"; };
216 247
 		50F5DFC31F407AA0001A00BC /* RNNNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNNavigationController.h; sourceTree = "<group>"; };
@@ -254,8 +285,8 @@
254 285
 		7BEF0D1B1E43771B003E96B0 /* RNNLayoutNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNLayoutNode.m; sourceTree = "<group>"; };
255 286
 		A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopBarOptions.m; sourceTree = "<group>"; };
256 287
 		A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopBarOptions.h; sourceTree = "<group>"; };
257
-		A7626BFF1FC578AB00492FB8 /* RNNTabBarOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTabBarOptions.h; sourceTree = "<group>"; };
258
-		A7626C001FC5796200492FB8 /* RNNTabBarOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTabBarOptions.m; sourceTree = "<group>"; };
288
+		A7626BFF1FC578AB00492FB8 /* RNNBottomTabsOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBottomTabsOptions.h; sourceTree = "<group>"; };
289
+		A7626C001FC5796200492FB8 /* RNNBottomTabsOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBottomTabsOptions.m; sourceTree = "<group>"; };
259 290
 		D8AFADBD1BEE6F3F00A4592D /* libReactNativeNavigation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReactNativeNavigation.a; sourceTree = BUILT_PRODUCTS_DIR; };
260 291
 		E8367B7E1F7A8A4700675C05 /* VICMAImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VICMAImageView.h; sourceTree = "<group>"; };
261 292
 		E8367B7F1F7A8A4700675C05 /* VICMAImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VICMAImageView.m; sourceTree = "<group>"; };
@@ -396,11 +427,44 @@
396 427
 			path = Animations;
397 428
 			sourceTree = "<group>";
398 429
 		};
430
+		504AFE611FFE52EF0076E904 /* Options */ = {
431
+			isa = PBXGroup;
432
+			children = (
433
+				504AFE621FFE53070076E904 /* RNNOptions.h */,
434
+				504AFE631FFE53070076E904 /* RNNOptions.m */,
435
+				E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */,
436
+				E83BAD6A1F27363A00A9F3DD /* RNNNavigationOptions.m */,
437
+				A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */,
438
+				A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */,
439
+				A7626BFF1FC578AB00492FB8 /* RNNBottomTabsOptions.h */,
440
+				A7626C001FC5796200492FB8 /* RNNBottomTabsOptions.m */,
441
+				50EB933F1FE14A3E00BD8EEE /* RNNBottomTabOptions.h */,
442
+				50EB93401FE14A3E00BD8EEE /* RNNBottomTabOptions.m */,
443
+				507F43C71FF4F9CC00D9425B /* RNNTopTabOptions.h */,
444
+				507F43C81FF4F9CC00D9425B /* RNNTopTabOptions.m */,
445
+				504AFE721FFFF0540076E904 /* RNNTopTabsOptions.h */,
446
+				504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */,
447
+				50CB3B671FDE911400AA153B /* RNNSideMenuOptions.h */,
448
+				50CB3B681FDE911400AA153B /* RNNSideMenuOptions.m */,
449
+			);
450
+			name = Options;
451
+			sourceTree = "<group>";
452
+		};
453
+		507F43F11FF4FCD800D9425B /* HMSegmentControl */ = {
454
+			isa = PBXGroup;
455
+			children = (
456
+				507F43F21FF4FCFE00D9425B /* HMSegmentedControl.h */,
457
+				507F43F31FF4FCFE00D9425B /* HMSegmentedControl.m */,
458
+			);
459
+			name = HMSegmentControl;
460
+			sourceTree = "<group>";
461
+		};
399 462
 		7B1E4C4B1E2D173700C3A525 /* Controllers */ = {
400 463
 			isa = PBXGroup;
401 464
 			children = (
402 465
 				E8AEDB471F584175000F5A6A /* Components */,
403 466
 				E8AEDB461F58414D000F5A6A /* Animations */,
467
+				504AFE611FFE52EF0076E904 /* Options */,
404 468
 				261F0E621E6EC94900989DE2 /* RNNModalManager.h */,
405 469
 				261F0E631E6EC94900989DE2 /* RNNModalManager.m */,
406 470
 				261F0E681E6F028A00989DE2 /* RNNNavigationStackManager.h */,
@@ -424,20 +488,15 @@
424 488
 				7BC9346D1E26886E00EFA125 /* RNNControllerFactory.m */,
425 489
 				263905E41E4CAC950023D7D3 /* RNNSideMenuChildVC.h */,
426 490
 				263905E51E4CAC950023D7D3 /* RNNSideMenuChildVC.m */,
427
-				E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */,
428
-				E83BAD6A1F27363A00A9F3DD /* RNNNavigationOptions.m */,
429 491
 				21B85E5E1F44482A00B314B5 /* RNNNavigationButtons.h */,
430 492
 				21B85E5C1F44480200B314B5 /* RNNNavigationButtons.m */,
431 493
 				214545261F4DC164006E8DA1 /* RNNUIBarButtonItem.h */,
432 494
 				214545241F4DC125006E8DA1 /* RNNUIBarButtonItem.m */,
433
-				50EB933F1FE14A3E00BD8EEE /* RNNTabItemOptions.h */,
434
-				50EB93401FE14A3E00BD8EEE /* RNNTabItemOptions.m */,
435
-				A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */,
436
-				A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */,
437
-				A7626BFF1FC578AB00492FB8 /* RNNTabBarOptions.h */,
438
-				A7626C001FC5796200492FB8 /* RNNTabBarOptions.m */,
439
-				50CB3B671FDE911400AA153B /* RNNSideMenuOptions.h */,
440
-				50CB3B681FDE911400AA153B /* RNNSideMenuOptions.m */,
495
+				507F43C31FF4F17C00D9425B /* RNNTopTabsViewController.h */,
496
+				507F43C41FF4F17C00D9425B /* RNNTopTabsViewController.m */,
497
+				507F43F61FF525B500D9425B /* RNNSegmentedControl.h */,
498
+				507F43F71FF525B500D9425B /* RNNSegmentedControl.m */,
499
+				507F441F1FFA8A8800D9425B /* RNNRootViewProtocol.h */,
441 500
 			);
442 501
 			name = Controllers;
443 502
 			sourceTree = "<group>";
@@ -498,6 +557,8 @@
498 557
 				214545271F4DC7ED006E8DA1 /* Helpers */,
499 558
 				7BA500731E2544B9001B9E1B /* ReactNativeNavigation.h */,
500 559
 				7BA500741E2544B9001B9E1B /* ReactNativeNavigation.m */,
560
+				2DCD9193200014A900EDC75D /* RNNBridgeManager.h */,
561
+				2DCD9194200014A900EDC75D /* RNNBridgeManager.m */,
501 562
 				268692801E5054F800E2C612 /* RNNStore.h */,
502 563
 				268692811E5054F800E2C612 /* RNNStore.m */,
503 564
 				7B4928061E70415400555040 /* RNNCommandsHandler.h */,
@@ -505,6 +566,7 @@
505 566
 				7BD721F31E2D3AA100724059 /* Bridge */,
506 567
 				7B1E4C4B1E2D173700C3A525 /* Controllers */,
507 568
 				263905881E4C6F440023D7D3 /* RNNSideMenu */,
569
+				507F43F11FF4FCD800D9425B /* HMSegmentControl */,
508 570
 				7B49FEBC1E95090800DEB3EA /* ReactNativeNavigationTests */,
509 571
 				D8AFADBE1BEE6F3F00A4592D /* Products */,
510 572
 				7B49FED01E950A7A00DEB3EA /* Frameworks */,
@@ -572,6 +634,8 @@
572 634
 			buildActionMask = 2147483647;
573 635
 			files = (
574 636
 				261F0E6A1E6F028A00989DE2 /* RNNNavigationStackManager.h in Headers */,
637
+				504AFE761FFFF1E00076E904 /* RNNNavigationOptions.h in Headers */,
638
+				504AFE771FFFF1E20076E904 /* RNNTopBarOptions.h in Headers */,
575 639
 				26916C981E4B9E7700D13680 /* RNNReactRootViewCreator.h in Headers */,
576 640
 				263905B01E4C6F440023D7D3 /* MMDrawerController+Subclass.h in Headers */,
577 641
 				E8AEDB4A1F5C0BAF000F5A6A /* RNNInteractivePopAnimator.h in Headers */,
@@ -579,6 +643,7 @@
579 643
 				263905B71E4C6F440023D7D3 /* UIViewController+MMDrawerController.h in Headers */,
580 644
 				263905BB1E4C6F440023D7D3 /* RCCDrawerHelper.h in Headers */,
581 645
 				263905CC1E4C6F440023D7D3 /* SidebarWunderlistAnimation.h in Headers */,
646
+				507F43F41FF4FCFE00D9425B /* HMSegmentedControl.h in Headers */,
582 647
 				7B4928081E70415400555040 /* RNNCommandsHandler.h in Headers */,
583 648
 				263905AE1E4C6F440023D7D3 /* MMDrawerBarButtonItem.h in Headers */,
584 649
 				50F5DFC11F407A8C001A00BC /* RNNTabBarController.h in Headers */,
@@ -587,6 +652,7 @@
587 652
 				263905C21E4C6F440023D7D3 /* SidebarAnimation.h in Headers */,
588 653
 				E8E518361F83B94A000467AC /* RNNViewLocation.h in Headers */,
589 654
 				263905B51E4C6F440023D7D3 /* MMExampleDrawerVisualStateManager.h in Headers */,
655
+				507F43C51FF4F17C00D9425B /* RNNTopTabsViewController.h in Headers */,
590 656
 				263905C61E4C6F440023D7D3 /* SidebarFeedlyAnimation.h in Headers */,
591 657
 				7B1126A31E2D2B6C00F9B03B /* RNNSplashScreen.h in Headers */,
592 658
 				261F0E641E6EC94900989DE2 /* RNNModalManager.h in Headers */,
@@ -607,18 +673,23 @@
607 673
 				268692821E5054F800E2C612 /* RNNStore.h in Headers */,
608 674
 				E8AEDB3C1F55A1C2000F5A6A /* RNNElementView.h in Headers */,
609 675
 				E8E518321F83B3E0000467AC /* RNNUtils.h in Headers */,
676
+				507F44201FFA8A8800D9425B /* RNNRootViewProtocol.h in Headers */,
610 677
 				263905CA1E4C6F440023D7D3 /* SidebarLuvocracyAnimation.h in Headers */,
611 678
 				263905B11E4C6F440023D7D3 /* MMDrawerController.h in Headers */,
612
-				50EB93411FE14A3E00BD8EEE /* RNNTabItemOptions.h in Headers */,
679
+				504AFE641FFE53070076E904 /* RNNOptions.h in Headers */,
613 680
 				263905B91E4C6F440023D7D3 /* RCCDrawerController.h in Headers */,
614 681
 				263905B31E4C6F440023D7D3 /* MMDrawerVisualState.h in Headers */,
615 682
 				E8A5CD621F49114F00E89D0D /* RNNElement.h in Headers */,
683
+				504AFE741FFFF0540076E904 /* RNNTopTabsOptions.h in Headers */,
616 684
 				E8E5182E1F83A48B000467AC /* RNNTransitionStateHolder.h in Headers */,
685
+				2DCD9195200014A900EDC75D /* RNNBridgeManager.h in Headers */,
617 686
 				7B1126A91E2D2B6C00F9B03B /* RNNControllerFactory.h in Headers */,
618 687
 				263905D61E4C94970023D7D3 /* RNNSideMenuController.h in Headers */,
619 688
 				263905C81E4C6F440023D7D3 /* SidebarFlipboardAnimation.h in Headers */,
620 689
 				7BEF0D1C1E43771B003E96B0 /* RNNLayoutNode.h in Headers */,
690
+				507F43C91FF4F9CC00D9425B /* RNNTopTabOptions.h in Headers */,
621 691
 				E8A5CD521F464F0400E89D0D /* RNNAnimator.h in Headers */,
692
+				507F43F81FF525B500D9425B /* RNNSegmentedControl.h in Headers */,
622 693
 			);
623 694
 			runOnlyForDeploymentPostprocessing = 0;
624 695
 		};
@@ -750,28 +821,35 @@
750 821
 				7BBFE5441E25330E002A6182 /* RNNBridgeModule.m in Sources */,
751 822
 				261F0E651E6EC94900989DE2 /* RNNModalManager.m in Sources */,
752 823
 				E8A5CD631F49114F00E89D0D /* RNNElement.m in Sources */,
824
+				504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */,
753 825
 				7BEF0D1D1E43771B003E96B0 /* RNNLayoutNode.m in Sources */,
754 826
 				7BA500781E254908001B9E1B /* RNNSplashScreen.m in Sources */,
827
+				504AFE651FFE53070076E904 /* RNNOptions.m in Sources */,
755 828
 				263905BA1E4C6F440023D7D3 /* RCCDrawerController.m in Sources */,
756 829
 				50F5DFC21F407A8C001A00BC /* RNNTabBarController.m in Sources */,
757 830
 				263905BC1E4C6F440023D7D3 /* RCCDrawerHelper.m in Sources */,
758 831
 				2145452A1F4DC85F006E8DA1 /* RCTHelpers.m in Sources */,
832
+				2DCD9196200014A900EDC75D /* RNNBridgeManager.m in Sources */,
759 833
 				263905C11E4C6F440023D7D3 /* SidebarAirbnbAnimation.m in Sources */,
760 834
 				263905D71E4C94970023D7D3 /* RNNSideMenuController.m in Sources */,
835
+				507F43CA1FF4F9CC00D9425B /* RNNTopTabOptions.m in Sources */,
761 836
 				26916C991E4B9E7700D13680 /* RNNReactRootViewCreator.m in Sources */,
762 837
 				214545251F4DC125006E8DA1 /* RNNUIBarButtonItem.m in Sources */,
763 838
 				263905B81E4C6F440023D7D3 /* UIViewController+MMDrawerController.m in Sources */,
764 839
 				263905CD1E4C6F440023D7D3 /* SidebarWunderlistAnimation.m in Sources */,
765 840
 				263905CF1E4C6F440023D7D3 /* TheSidebarController.m in Sources */,
766 841
 				E8A430121F9CB87B00B61A20 /* RNNAnimatedView.m in Sources */,
767
-				50EB93421FE14A3E00BD8EEE /* RNNTabItemOptions.m in Sources */,
842
+				507F43F51FF4FCFE00D9425B /* HMSegmentedControl.m in Sources */,
843
+				507F43C61FF4F17C00D9425B /* RNNTopTabsViewController.m in Sources */,
844
+				50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */,
768 845
 				E8367B811F7A8A4700675C05 /* VICMAImageView.m in Sources */,
769
-				A7626C011FC5796200492FB8 /* RNNTabBarOptions.m in Sources */,
846
+				A7626C011FC5796200492FB8 /* RNNBottomTabsOptions.m in Sources */,
770 847
 				263905AF1E4C6F440023D7D3 /* MMDrawerBarButtonItem.m in Sources */,
771 848
 				E8AEDB4B1F5C0BAF000F5A6A /* RNNInteractivePopAnimator.m in Sources */,
772 849
 				7B4928091E70415400555040 /* RNNCommandsHandler.m in Sources */,
773 850
 				268692831E5054F800E2C612 /* RNNStore.m in Sources */,
774 851
 				7BC9346E1E26886E00EFA125 /* RNNControllerFactory.m in Sources */,
852
+				507F43F91FF525B500D9425B /* RNNSegmentedControl.m in Sources */,
775 853
 				E8E5182F1F83A48B000467AC /* RNNTransitionStateHolder.m in Sources */,
776 854
 				263905B61E4C6F440023D7D3 /* MMExampleDrawerVisualStateManager.m in Sources */,
777 855
 				E8E518331F83B3E0000467AC /* RNNUtils.m in Sources */,

+ 2
- 4
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m View File

@@ -10,7 +10,6 @@
10 10
 
11 11
 @property (nonatomic, strong) RNNStore* store;
12 12
 @property (nonatomic, strong) RNNCommandsHandler* uut;
13
-@property (nonatomic, strong) RCTBridge* bridge;
14 13
 
15 14
 @end
16 15
 
@@ -20,8 +19,7 @@
20 19
 	[super setUp];
21 20
 //	[self.store setReadyToReceiveCommands:true];
22 21
 	self.store = [[RNNStore alloc] init];
23
-	self.bridge = nil;
24
-	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:nil andBridge:self.bridge];
22
+	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:nil];
25 23
 }
26 24
 
27 25
 
@@ -40,7 +38,7 @@
40 38
 -(NSArray*) getPublicMethodNamesForObject:(NSObject*)obj{
41 39
 	NSMutableArray* skipMethods = [NSMutableArray new];
42 40
 
43
-	[skipMethods addObject:@"initWithStore:controllerFactory:andBridge:"];
41
+	[skipMethods addObject:@"initWithStore:controllerFactory:"];
44 42
 	[skipMethods addObject:@"assertReady"];
45 43
 	[skipMethods addObject:@".cxx_destruct"];
46 44
 

+ 1
- 1
lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m View File

@@ -21,7 +21,7 @@
21 21
 	[super setUp];
22 22
 	self.creator = nil;
23 23
 	self.store = [RNNStore new];
24
-	self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator store:self.store eventEmitter:nil];
24
+	self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator store:self.store eventEmitter:nil andBridge:nil];
25 25
 }
26 26
 
27 27
 - (void)tearDown {

+ 3
- 3
lib/ios/ReactNativeNavigationTests/RNNNavigationOptionsTest.m View File

@@ -19,9 +19,8 @@
19 19
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{@"topBar": @{@"backgroundColor" : @(0xff0000ff)}}];
20 20
 	XCTAssertTrue(options.topBar.backgroundColor);
21 21
 }
22
--(void)testReturnsNilWhenStyleDoesNotExist{
23
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{@"topBar": @{@"someColor" : @(0xff0000ff)}}];
24
-	XCTAssertNil(options.topBar.backgroundColor);
22
+-(void)testThrowsWhenStyleDoesNotExist{
23
+	XCTAssertThrows([[RNNNavigationOptions alloc] initWithDict:@{@"topBar": @{@"someColor" : @(0xff0000ff)}}]);
25 24
 }
26 25
 
27 26
 -(void)testChangeRNNNavigationOptionsDynamically{
@@ -36,4 +35,5 @@
36 35
 	NSDictionary* dynamicOptions = @{@"topBar": @{@"titleeeee" : @"hello"}};
37 36
 	XCTAssertThrows([options mergeWith:dynamicOptions]);
38 37
 }
38
+
39 39
 @end

+ 5
- 5
lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m View File

@@ -174,7 +174,7 @@
174 174
 
175 175
 -(void)testTabBadge {
176 176
 	NSString* tabBadgeInput = @"5";
177
-	self.options.tabItem.badge = tabBadgeInput;
177
+	self.options.bottomTab.badge = tabBadgeInput;
178 178
 	__unused RNNTabBarController* vc = [[RNNTabBarController alloc] init];
179 179
 	NSMutableArray* controllers = [NSMutableArray new];
180 180
 	UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"A Tab" image:nil tag:1];
@@ -355,7 +355,7 @@
355 355
 }
356 356
 
357 357
 -(void)testRightButtonsWithTitle_withoutStyle {
358
-	self.options.rightButtons = @[@{@"id": @"testId", @"title": @"test"}];
358
+	self.options.topBar.rightButtons = @[@{@"id": @"testId", @"title": @"test"}];
359 359
 	__unused UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:self.uut];
360 360
 	[self.uut viewWillAppear:false];
361 361
 	
@@ -370,7 +370,7 @@
370 370
 -(void)testRightButtonsWithTitle_withStyle {
371 371
 	NSNumber* inputColor = @(0xFFFF0000);
372 372
 	
373
-	self.options.rightButtons = @[@{@"id": @"testId", @"title": @"test", @"disabled": @true, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
373
+	self.options.topBar.rightButtons = @[@{@"id": @"testId", @"title": @"test", @"disabled": @true, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
374 374
 	__unused UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:self.uut];
375 375
 	[self.uut viewWillAppear:false];
376 376
 	
@@ -386,7 +386,7 @@
386 386
 
387 387
 
388 388
 -(void)testLeftButtonsWithTitle_withoutStyle {
389
-	self.options.leftButtons = @[@{@"id": @"testId", @"title": @"test"}];
389
+	self.options.topBar.leftButtons = @[@{@"id": @"testId", @"title": @"test"}];
390 390
 	__unused UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:self.uut];
391 391
 	[self.uut viewWillAppear:false];
392 392
 	
@@ -401,7 +401,7 @@
401 401
 -(void)testLeftButtonsWithTitle_withStyle {
402 402
 	NSNumber* inputColor = @(0xFFFF0000);
403 403
 	
404
-	self.options.leftButtons = @[@{@"id": @"testId", @"title": @"test", @"disabled": @true, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
404
+	self.options.topBar.leftButtons = @[@{@"id": @"testId", @"title": @"test", @"disabled": @true, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
405 405
 	__unused UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:self.uut];
406 406
 	[self.uut viewWillAppear:false];
407 407
 	

+ 2
- 0
lib/src/params/containers/Container.js View File

@@ -5,6 +5,7 @@ class Container {
5 5
    * @property {string} name The container's registered name
6 6
    * @property {Container[]} [topTabs]
7 7
    * @property {object} [passProps] props
8
+   * @property {object} [customTransition]
8 9
    * @property {Options} options
9 10
    */
10 11
   constructor(params) {
@@ -14,6 +15,7 @@ class Container {
14 15
       this.topTabs = params.topTabs;
15 16
     }
16 17
     this.passProps = params.passProps;
18
+    this.customTransition = params.customTransition;
17 19
     this.options = params.options && new Options(params.options);
18 20
   }
19 21
 }

+ 0
- 5
lib/src/params/options/Options.js View File

@@ -1,6 +1,5 @@
1 1
 const TopBar = require('./TopBar');
2 2
 const BottomTabs = require('./BottomTabs');
3
-const Button = require('./Button');
4 3
 const BottomTab = require('./BottomTab');
5 4
 const TopTabs = require('./TopTabs');
6 5
 
@@ -11,16 +10,12 @@ class NavigationOptions {
11 10
    * @property {options:BottomTab} [bottomTab]
12 11
    * @property {string} [orientation]
13 12
    * @property {options:TopTabs} [topTabs]
14
-   * @property {options:Button[]} [rightButtons]
15
-   * @property {options:Button[]} [leftButtons]
16 13
    */
17 14
   constructor(options) {
18 15
     this.topBar = options.topBar && new TopBar(options.topBar);
19 16
     this.bottomTabs = options.bottomTabs && new BottomTabs(options.bottomTabs);
20 17
     this.bottomTab = options.bottomTab && new BottomTab(options.bottomTab);
21 18
     this.orientation = options.orientation;
22
-    this.rightButtons = options.rightButtons && options.rightButtons.map((button) => new Button(button));
23
-    this.leftButtons = options.leftButtons && options.leftButtons.map((button) => new Button(button));
24 19
     this.sideMenu = options.sideMenu;
25 20
     this.backgroundImage = options.backgroundImage;
26 21
     this.rootBackgroundImage = options.rootBackgroundImage;

+ 0
- 20
lib/src/params/options/Options.test.js View File

@@ -7,22 +7,6 @@ const TopTabs = require('./TopTabs');
7 7
 const TAB_BAR = {};
8 8
 const TOP_BAR = {};
9 9
 const TAB_ITEM = {};
10
-const RIGHT_BUTTONS = [
11
-  {
12
-    id: 'myBtn',
13
-    testID: 'BTN',
14
-    title: 'My Button',
15
-    buttonColor: 'red'
16
-  }
17
-];
18
-const LEFT_BUTTONS = [
19
-  {
20
-    id: 'myBtn',
21
-    testID: 'BTN',
22
-    title: 'My Button',
23
-    buttonColor: 'red'
24
-  }
25
-];
26 10
 const TOP_TABS = {
27 11
   selectedTabColor: 'blue',
28 12
   unselectedTabColor: 'red'
@@ -32,8 +16,6 @@ const NAVIGATION_OPTIONS = {
32 16
   bottomTabs: TAB_BAR,
33 17
   bottomTab: TAB_ITEM,
34 18
   orientation: 'portrait',
35
-  rightButtons: RIGHT_BUTTONS,
36
-  leftButtons: LEFT_BUTTONS,
37 19
   topTabs: TOP_TABS
38 20
 };
39 21
 
@@ -44,8 +26,6 @@ describe('NavigationOptions', () => {
44 26
     expect(uut.topBar).toBeInstanceOf(TopBar);
45 27
     expect(uut.bottomTab).toBeInstanceOf(BottomTab);
46 28
     expect(uut.orientation).toEqual('portrait');
47
-    expect(uut.rightButtons).toEqual(RIGHT_BUTTONS);
48
-    expect(uut.leftButtons).toEqual(LEFT_BUTTONS);
49 29
     expect(uut.topTabs).toBeInstanceOf(TopTabs);
50 30
   });
51 31
 });

+ 6
- 0
lib/src/params/options/TopBar.js View File

@@ -1,3 +1,5 @@
1
+const Button = require('./Button');
2
+
1 3
 class TopBar {
2 4
   /**
3 5
    * @property {string} [title]
@@ -16,6 +18,8 @@ class TopBar {
16 18
    * @property {boolean} [noBorder]
17 19
    * @property {boolean} [largeTitle]
18 20
    * @property {boolean} [drawUnder]
21
+   * @property {options:Button[]} [rightButtons]
22
+   * @property {options:Button[]} [leftButtons]
19 23
    */
20 24
   constructor(options) {
21 25
     this.title = options.title;
@@ -34,6 +38,8 @@ class TopBar {
34 38
     this.largeTitle = options.largeTitle;
35 39
     this.testID = options.testID;
36 40
     this.drawUnder = options.drawUnder;
41
+    this.rightButtons = options.rightButtons && options.rightButtons.map((button) => new Button(button));
42
+    this.leftButtons = options.leftButtons && options.leftButtons.map((button) => new Button(button));
37 43
   }
38 44
 }
39 45
 

+ 22
- 1
lib/src/params/options/TopBar.test.js View File

@@ -1,4 +1,22 @@
1 1
 const TopBar = require('./TopBar');
2
+const Button = require('./Button');
3
+
4
+const RIGHT_BUTTONS = [
5
+  {
6
+    id: 'myBtn',
7
+    testID: 'BTN',
8
+    title: 'My Button',
9
+    buttonColor: 'red'
10
+  }
11
+];
12
+const LEFT_BUTTONS = [
13
+  {
14
+    id: 'myBtn',
15
+    testID: 'BTN',
16
+    title: 'My Button',
17
+    buttonColor: 'red'
18
+  }
19
+];
2 20
 
3 21
 const TOP_BAR = {
4 22
   title: 'something',
@@ -9,7 +27,9 @@ const TOP_BAR = {
9 27
   hidden: true,
10 28
   animateHide: true,
11 29
   hideOnScroll: true,
12
-  transparent: true
30
+  transparent: true,
31
+  rightButtons: RIGHT_BUTTONS,
32
+  leftButtons: LEFT_BUTTONS
13 33
 };
14 34
 
15 35
 describe('TopBar', () => {
@@ -24,5 +44,6 @@ describe('TopBar', () => {
24 44
     expect(uut.animateHide).toEqual(true);
25 45
     expect(uut.hideOnScroll).toEqual(true);
26 46
     expect(uut.transparent).toEqual(true);
47
+    expect(uut.leftButtons[0]).toBeInstanceOf(Button);
27 48
   });
28 49
 });

+ 46
- 42
playground/src/containers/OptionsScreen.js View File

@@ -20,22 +20,22 @@ class OptionsScreen extends Component {
20 20
         hidden: false,
21 21
         textFontSize: 16,
22 22
         textFontFamily: 'HelveticaNeue-Italic',
23
-        testID: testIDs.TOP_BAR_ELEMENT
24
-      },
25
-      rightButtons: [{
26
-        id: BUTTON_ONE,
27
-        testID: BUTTON_ONE,
28
-        title: 'One',
29
-        buttonFontSize: 28,
30
-        buttonColor: 'red'
31
-      }],
32
-      leftButtons: [{
33
-        id: BUTTON_LEFT,
34
-        testID: BUTTON_LEFT,
35
-        icon: require('../../img/navicon_add.png'),
36
-        title: 'Left',
37
-        buttonColor: 'purple'
38
-      }]
23
+        testID: testIDs.TOP_BAR_ELEMENT,
24
+        rightButtons: [{
25
+          id: BUTTON_ONE,
26
+          testID: BUTTON_ONE,
27
+          title: 'One',
28
+          buttonFontSize: 28,
29
+          buttonColor: 'red'
30
+        }],
31
+        leftButtons: [{
32
+          id: BUTTON_LEFT,
33
+          testID: BUTTON_LEFT,
34
+          icon: require('../../img/navicon_add.png'),
35
+          title: 'Left',
36
+          buttonColor: 'purple'
37
+        }]
38
+      }
39 39
     };
40 40
   }
41 41
 
@@ -71,35 +71,39 @@ class OptionsScreen extends Component {
71 71
   onNavigationButtonPressed(id) {
72 72
     if (id === BUTTON_ONE) {
73 73
       Navigation.setOptions(this.props.containerId, {
74
-        rightButtons: [{
75
-          id: BUTTON_TWO,
76
-          testID: BUTTON_TWO,
77
-          title: 'Two',
78
-          icon: require('../../img/navicon_add.png'),
79
-          disableIconTint: true,
80
-          // disabled: true,
81
-          showAsAction: 'ifRoom',
82
-          buttonColor: 'green',
83
-          buttonFontSize: 28,
84
-          buttonFontWeight: '800'
85
-        }],
86
-        leftButtons: []
74
+        topBar: {
75
+          rightButtons: [{
76
+            id: BUTTON_TWO,
77
+            testID: BUTTON_TWO,
78
+            title: 'Two',
79
+            icon: require('../../img/navicon_add.png'),
80
+            disableIconTint: true,
81
+            // disabled: true,
82
+            showAsAction: 'ifRoom',
83
+            buttonColor: 'green',
84
+            buttonFontSize: 28,
85
+            buttonFontWeight: '800'
86
+          }],
87
+          leftButtons: []
88
+        }
87 89
       });
88 90
     } else if (id === BUTTON_TWO) {
89 91
       Navigation.setOptions(this.props.containerId, {
90
-        rightButtons: [{
91
-          id: BUTTON_ONE,
92
-          testID: BUTTON_ONE,
93
-          title: 'One',
94
-          buttonColor: 'red'
95
-        }],
96
-        leftButtons: [{
97
-          id: BUTTON_LEFT,
98
-          testID: BUTTON_LEFT,
99
-          icon: require('../../img/navicon_add.png'),
100
-          title: 'Left',
101
-          buttonColor: 'purple'
102
-        }]
92
+        topBar: {
93
+          rightButtons: [{
94
+            id: BUTTON_ONE,
95
+            testID: BUTTON_ONE,
96
+            title: 'One',
97
+            buttonColor: 'red'
98
+          }],
99
+          leftButtons: [{
100
+            id: BUTTON_LEFT,
101
+            testID: BUTTON_LEFT,
102
+            icon: require('../../img/navicon_add.png'),
103
+            title: 'Left',
104
+            buttonColor: 'purple'
105
+          }]
106
+        }
103 107
       });
104 108
     } else if (id === BUTTON_LEFT) {
105 109
       Navigation.pop(this.props.containerId);

+ 1
- 1
playground/src/containers/WelcomeScreen.js View File

@@ -183,7 +183,7 @@ class WelcomeScreen extends Component {
183 183
           options: {
184 184
             topTab: {
185 185
               title: 'Tab 2',
186
-              titleFontFamily: 'Dosis-Regular'
186
+              titleFontFamily: 'HelveticaNeue-Italic'
187 187
             }
188 188
           }
189 189
         },