OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #import "ios/clean/chrome/browser/ui/tab_strip/tab_strip_container_view_controll
er.h" | |
6 | |
7 #import "base/mac/foundation_util.h" | |
8 #import "ios/clean/chrome/browser/ui/actions/tab_strip_actions.h" | |
9 #import "ios/clean/chrome/browser/ui/ui_types.h" | |
10 | |
11 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
12 #error "This file requires ARC support." | |
13 #endif | |
14 | |
15 namespace { | |
16 CGFloat kStripHeight = 200.0; | |
17 } | |
18 | |
19 @interface TabStripContainerViewController ()<TabStripActions> | |
20 | |
21 // Whichever view controller is at the top of the screen. This view controller | |
22 // controls the status bar. | |
23 @property(nonatomic, weak) UIViewController* topmostViewController; | |
24 | |
25 @property(nonatomic, strong) Constraints* contentConstraintsWithStrip; | |
26 @property(nonatomic, strong) Constraints* contentConstraintsWithoutStrip; | |
27 @property(nonatomic, strong) Constraints* stripConstraints; | |
28 | |
29 // Cache for forwarding methods to child view controllers. | |
30 @property(nonatomic, assign) SEL actionToForward; | |
31 @property(nonatomic, weak) UIResponder* forwardingTarget; | |
32 | |
33 @property(nonatomic, strong) NSLayoutConstraint* stripHeightConstraint; | |
34 | |
35 // Contained view controller utility methods. This method cannot be named | |
36 //-removeChildViewController:, as that is a private superclass method. | |
37 - (void)detachChildViewController:(UIViewController*)viewController; | |
38 | |
39 // Called after a new content view controller is set, but before | |
40 // |-didMoveToParentViewController:| is called on that view controller. | |
41 - (void)didAddContentViewController; | |
42 | |
43 // Called after a new strip view controller is set, but before | |
44 // |-didMoveToParentViewController:| is called on that view controller. | |
45 - (void)didAddTabStripViewController; | |
46 | |
47 // Methods to populate the constraint properties. | |
48 - (void)updateContentConstraintsWithStrip; | |
49 - (void)updateContentConstraintsWithoutStrip; | |
50 - (void)updateStripConstraints; | |
51 | |
52 @end | |
53 | |
54 @implementation TabStripContainerViewController | |
55 | |
56 @synthesize contentViewController = _contentViewController; | |
57 @synthesize tabStripViewController = _tabStripViewController; | |
58 @synthesize topmostViewController = _topmostViewController; | |
59 @synthesize contentConstraintsWithStrip = _contentConstraintsWithStrip; | |
60 @synthesize contentConstraintsWithoutStrip = _contentConstraintsWithoutStrip; | |
61 @synthesize stripConstraints = _stripConstraints; | |
62 @synthesize actionToForward = _actionToForward; | |
63 @synthesize forwardingTarget = _forwardingTarget; | |
64 @synthesize stripHeightConstraint = _stripHeightConstraint; | |
65 | |
66 #pragma mark - Public properties | |
67 | |
68 - (void)setContentViewController:(UIViewController*)contentViewController { | |
69 if (self.contentViewController == contentViewController) | |
70 return; | |
71 | |
72 // Remove the current content view controller, if any. | |
73 [NSLayoutConstraint | |
74 deactivateConstraints:self.contentConstraintsWithoutStrip]; | |
75 [NSLayoutConstraint deactivateConstraints:self.contentConstraintsWithStrip]; | |
76 [self detachChildViewController:self.contentViewController]; | |
77 | |
78 // Add the new content view controller. | |
79 [self addChildViewController:contentViewController]; | |
80 contentViewController.view.translatesAutoresizingMaskIntoConstraints = NO; | |
81 [self.view addSubview:contentViewController.view]; | |
82 _contentViewController = contentViewController; | |
83 [self didAddContentViewController]; | |
84 [self.view setNeedsUpdateConstraints]; | |
85 [self.contentViewController didMoveToParentViewController:self]; | |
86 } | |
87 | |
88 - (void)setTabStripViewController:(UIViewController*)tabStripViewController { | |
89 if (self.tabStripViewController == tabStripViewController) | |
90 return; | |
91 | |
92 // Remove the current strip view controller, if any. | |
93 [NSLayoutConstraint deactivateConstraints:self.stripConstraints]; | |
94 [NSLayoutConstraint deactivateConstraints:self.contentConstraintsWithStrip]; | |
95 [self detachChildViewController:self.tabStripViewController]; | |
96 | |
97 // Add the new strip view controller. | |
98 [self addChildViewController:tabStripViewController]; | |
99 tabStripViewController.view.translatesAutoresizingMaskIntoConstraints = NO; | |
100 [self.view addSubview:tabStripViewController.view]; | |
101 _tabStripViewController = tabStripViewController; | |
102 [self didAddTabStripViewController]; | |
103 [self.view setNeedsUpdateConstraints]; | |
104 [self.tabStripViewController didMoveToParentViewController:self]; | |
105 } | |
106 | |
107 #pragma mark - UIViewController | |
108 | |
109 - (void)updateViewConstraints { | |
110 if (self.tabStripViewController) { | |
111 [NSLayoutConstraint activateConstraints:self.stripConstraints]; | |
112 [NSLayoutConstraint activateConstraints:self.contentConstraintsWithStrip]; | |
113 } else { | |
114 [NSLayoutConstraint | |
115 activateConstraints:self.contentConstraintsWithoutStrip]; | |
116 } | |
117 [super updateViewConstraints]; | |
118 } | |
119 | |
120 - (UIViewController*)childViewControllerForStatusBarHidden { | |
121 return self.topmostViewController; | |
122 } | |
123 | |
124 - (UIViewController*)childViewControllerForStatusBarStyle { | |
125 return self.topmostViewController; | |
126 } | |
127 | |
128 #pragma mark - UIResponder | |
129 | |
130 // Before forwarding actions up the responder chain, give both contained | |
131 // view controllers a chance to handle them. | |
132 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { | |
133 self.actionToForward = nullptr; | |
134 self.forwardingTarget = nil; | |
135 for (UIResponder* responder in | |
136 @[ self.contentViewController, self.tabStripViewController ]) { | |
137 if ([responder canPerformAction:action withSender:sender]) { | |
138 self.actionToForward = action; | |
139 self.forwardingTarget = responder; | |
140 return YES; | |
141 } | |
142 } | |
143 return [super canPerformAction:action withSender:sender]; | |
144 } | |
145 | |
146 #pragma mark - NSObject method forwarding | |
147 | |
148 - (id)forwardingTargetForSelector:(SEL)aSelector { | |
149 if (aSelector == self.actionToForward) { | |
150 return self.forwardingTarget; | |
151 } | |
152 return nil; | |
153 } | |
154 | |
155 #pragma mark - TabStripActions | |
156 | |
157 // Action to toggle visibility of tab strip. | |
158 - (void)showTabStrip:(id)sender { | |
159 self.stripHeightConstraint.constant = kStripHeight; | |
160 } | |
161 | |
162 - (void)hideTabStrip:(id)sender { | |
163 self.stripHeightConstraint.constant = 0.0f; | |
164 } | |
165 | |
166 #pragma mark - MenuPresentationDelegate | |
167 | |
168 - (CGRect)frameForMenuPresentation:(UIPresentationController*)presentation { | |
169 CGSize menuSize = presentation.presentedView.frame.size; | |
170 CGRect menuRect; | |
171 menuRect.size = menuSize; | |
172 | |
173 CGRect menuOriginRect = [self rectForZoomWithKey:@"" inView:self.view]; | |
174 if (CGRectIsNull(menuOriginRect)) { | |
175 menuRect.origin = CGPointMake(50, 50); | |
176 return menuRect; | |
177 } | |
178 // Calculate which corner of the menu the origin rect is in. This is | |
179 // determined by comparing frames, and thus is RTL-independent. | |
180 if (CGRectGetMinX(menuOriginRect) - CGRectGetMinX(self.view.bounds) < | |
181 CGRectGetMaxX(self.view.bounds) - CGRectGetMaxX(menuOriginRect)) { | |
182 // Origin rect is closer to the left edge of |self.view| than to the right. | |
183 menuRect.origin.x = CGRectGetMinX(menuOriginRect); | |
184 } else { | |
185 // Origin rect is closer to the right edge of |self.view| than to the left. | |
186 menuRect.origin.x = CGRectGetMaxX(menuOriginRect) - menuSize.width; | |
187 } | |
188 | |
189 if (CGRectGetMinY(menuOriginRect) - CGRectGetMinY(self.view.bounds) < | |
190 CGRectGetMaxY(self.view.bounds) - CGRectGetMaxY(menuOriginRect)) { | |
191 // Origin rect is closer to the top edge of |self.view| than to the bottom. | |
192 menuRect.origin.y = CGRectGetMinY(menuOriginRect); | |
193 } else { | |
194 // Origin rect is closer to the bottom edge of |self.view| than to the top. | |
195 menuRect.origin.y = CGRectGetMaxY(menuOriginRect) - menuSize.height; | |
196 } | |
197 | |
198 return menuRect; | |
199 } | |
200 | |
201 #pragma mark - ZoomTransitionDelegate | |
202 | |
203 - (CGRect)rectForZoomWithKey:(NSObject*)key inView:(UIView*)view { | |
204 if ([self.contentViewController | |
205 conformsToProtocol:@protocol(ZoomTransitionDelegate)]) { | |
206 return [reinterpret_cast<id<ZoomTransitionDelegate>>( | |
207 self.contentViewController) rectForZoomWithKey:key | |
208 inView:view]; | |
209 } | |
210 return CGRectNull; | |
211 } | |
212 | |
213 #pragma mark - Private methods | |
214 | |
215 - (void)detachChildViewController:(UIViewController*)viewController { | |
216 if (viewController.parentViewController != self) | |
217 return; | |
218 [viewController willMoveToParentViewController:nil]; | |
219 [viewController.view removeFromSuperview]; | |
220 [viewController removeFromParentViewController]; | |
221 } | |
222 | |
223 - (void)didAddContentViewController { | |
224 if (self.tabStripViewController) { | |
225 [self updateContentConstraintsWithStrip]; | |
226 } else { | |
227 self.topmostViewController = self.contentViewController; | |
228 [self updateContentConstraintsWithoutStrip]; | |
229 } | |
230 } | |
231 | |
232 - (void)didAddTabStripViewController { | |
233 [self updateStripConstraints]; | |
234 // If there's already a content view controller, update the constraints for | |
235 // that, too. | |
236 if (self.contentViewController) { | |
237 [self updateContentConstraintsWithStrip]; | |
238 } | |
239 self.topmostViewController = self.tabStripViewController; | |
240 } | |
241 | |
242 - (void)updateContentConstraintsWithoutStrip { | |
243 UIView* contentView = self.contentViewController.view; | |
244 self.contentConstraintsWithoutStrip = @[ | |
245 [contentView.topAnchor constraintEqualToAnchor:self.view.topAnchor], | |
246 [contentView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], | |
247 [contentView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], | |
248 [contentView.trailingAnchor | |
249 constraintEqualToAnchor:self.view.trailingAnchor], | |
250 ]; | |
251 } | |
252 | |
253 - (void)updateContentConstraintsWithStrip { | |
254 UIView* contentView = self.contentViewController.view; | |
255 self.contentConstraintsWithStrip = @[ | |
256 [contentView.topAnchor | |
257 constraintEqualToAnchor:self.tabStripViewController.view.bottomAnchor], | |
258 [contentView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], | |
259 [contentView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], | |
260 [contentView.trailingAnchor | |
261 constraintEqualToAnchor:self.view.trailingAnchor], | |
262 ]; | |
263 } | |
264 | |
265 - (void)updateStripConstraints { | |
266 UIView* stripView = self.tabStripViewController.view; | |
267 self.stripHeightConstraint = | |
268 [stripView.heightAnchor constraintEqualToConstant:0.0]; | |
269 self.stripConstraints = @[ | |
270 [stripView.topAnchor constraintEqualToAnchor:self.view.topAnchor], | |
271 [stripView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], | |
272 [stripView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], | |
273 self.stripHeightConstraint, | |
274 ]; | |
275 } | |
276 | |
277 @end | |
OLD | NEW |