Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: ios/chrome/search_widget_extension/search_widget_view.mm

Issue 2858253002: Adding search widget UI and functionality. (Closed)
Patch Set: addressing comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ios/chrome/search_widget_extension/search_widget_view.h" 5 #import "ios/chrome/search_widget_extension/search_widget_view.h"
6
7 #include "base/logging.h" 6 #include "base/logging.h"
7 #import "ios/chrome/search_widget_extension/ui_util.h"
8 8
9 #if !defined(__has_feature) || !__has_feature(objc_arc) 9 #if !defined(__has_feature) || !__has_feature(objc_arc)
10 #error "This file requires ARC support." 10 #error "This file requires ARC support."
11 #endif 11 #endif
12 12
13 namespace { 13 namespace {
14 14
15 const CGFloat kCursorHeight = 40; 15 const CGFloat kContentMargin = 16;
16 const CGFloat kCursorWidth = 2; 16 const CGFloat kURLButtonMargin = 10;
17 const CGFloat kCursorHorizontalPadding = 10; 17 const CGFloat kActionButtonSize = 55;
18 const CGFloat kCursorVerticalPadding = 10; 18 const CGFloat kIconSize = 35;
19 const CGFloat kFakeboxHorizontalPadding = 40; 19 const CGFloat kMaxContentSize = 421;
20 const CGFloat kFakeboxVerticalPadding = 40; 20 const CGFloat kIconSpacing = 5;
21 21
22 } // namespace 22 } // namespace
23 23
24 @interface SearchWidgetView () { 24 @interface SearchWidgetView ()
25 __weak id<SearchWidgetViewActionTarget> _target; 25
26 } 26 // The target for actions in the view.
27 27 @property(nonatomic, weak) id<SearchWidgetViewActionTarget> target;
28 @property(nonatomic, copy) NSString* copiedURL; 28 // The copied URL label containing the URL or a placeholder text.
29 @property(nonatomic, strong) UILabel* copiedURLLabel; 29 @property(nonatomic, strong) UILabel* copiedURLLabel;
30 @property(nonatomic, weak) UIView* cursor; 30 // The copued URL title label containing the title of the copied URL button.
31 31 @property(nonatomic, strong) UILabel* openCopiedURLTitleLabel;
32 // Creates and adds a fake omnibox with blinking cursor to the view and sets the 32 // The hairline view shown between the action and copied URL views.
33 // class cursor property. 33 @property(nonatomic, strong) UIView* hairlineView;
34 - (void)addFakebox; 34 // The button shown when there is a copied URL to open.
35 @property(nonatomic, strong) UIButton* copiedButtonView;
36 // The primary effect view of the widget. Add views here for a more opaque
37 // appearance.
38 @property(nonatomic, strong) UIVisualEffectView* primaryEffectView;
39 // The secondary effect view of the widget. Add views here for a more
40 // transparent appearance.
41 @property(nonatomic, strong) UIVisualEffectView* secondaryEffectView;
42 // The constraints to be activated when the copiedURL section is visible.
43 @property(nonatomic, strong)
44 NSArray<NSLayoutConstraint*>* visibleCopiedURLConstraints;
45 // The constraints to be activated when the copiedURL section is hidden.
46 @property(nonatomic, strong)
47 NSArray<NSLayoutConstraint*>* hiddenCopiedURLConstraints;
48
49 // Sets up the widget UI.
50 - (void)createUI;
51
52 // Creates the view for the action buttons.
53 - (UIView*)newActionsView;
54
55 // Creates the view for the copiedURL section.
56 - (void)initializeOpenCopiedURLSectionUsingTopAnchor:(NSLayoutAnchor*)topAnchor;
35 57
36 @end 58 @end
37 59
38 @implementation SearchWidgetView 60 @implementation SearchWidgetView
39 61
40 @synthesize copiedURL = _copiedURL; 62 @synthesize target = _target;
63 @synthesize copiedURLVisible = _copiedURLVisible;
64 @synthesize copiedURLString = _copiedURLString;
41 @synthesize copiedURLLabel = _copiedURLLabel; 65 @synthesize copiedURLLabel = _copiedURLLabel;
42 66 @synthesize openCopiedURLTitleLabel = _openCopiedURLTitleLabel;
43 @synthesize cursor = _cursor; 67 @synthesize hairlineView = _hairlineView;
44 68 @synthesize copiedButtonView = _copiedButtonView;
45 - (instancetype)initWithActionTarget:(id<SearchWidgetViewActionTarget>)target { 69 @synthesize primaryEffectView = _primaryEffectView;
70 @synthesize secondaryEffectView = _secondaryEffectView;
71 @synthesize visibleCopiedURLConstraints = _visibleCopiedURLConstraints;
72 @synthesize hiddenCopiedURLConstraints = _hiddenCopiedURLConstraints;
73
74 - (instancetype)initWithActionTarget:(id<SearchWidgetViewActionTarget>)target
75 primaryVibrancyEffect:(UIVibrancyEffect*)primaryVibrancyEffect
76 secondaryVibrancyEffect:
77 (UIVibrancyEffect*)secondaryVibrancyEffect {
46 self = [super initWithFrame:CGRectZero]; 78 self = [super initWithFrame:CGRectZero];
47 if (self) { 79 if (self) {
48 DCHECK(target); 80 DCHECK(target);
49 _target = target; 81 _target = target;
50 [self addFakebox]; 82 _primaryEffectView =
83 [[UIVisualEffectView alloc] initWithEffect:primaryVibrancyEffect];
84 _secondaryEffectView =
85 [[UIVisualEffectView alloc] initWithEffect:secondaryVibrancyEffect];
86 _copiedURLVisible = YES;
87 [self createUI];
88 [self updateCopiedURLUI];
51 } 89 }
52 return self; 90 return self;
53 } 91 }
54 92
55 - (void)addFakebox { 93 #pragma mark - property overrides
56 UIView* fakebox = [[UIView alloc] initWithFrame:CGRectZero]; 94
57 95 - (void)setCopiedURLVisible:(BOOL)copiedURLVisible {
96 _copiedURLVisible = copiedURLVisible;
97 [self updateCopiedURLUI];
98 }
99
100 - (void)setCopiedURLString:(NSString*)copiedURL {
101 _copiedURLString = copiedURL;
102 [self updateCopiedURLUI];
103 }
104
105 #pragma mark - UI creation
106
107 - (void)createUI {
108 for (UIVisualEffectView* effectView in
109 @[ self.primaryEffectView, self.secondaryEffectView ]) {
110 [self addSubview:effectView];
111 effectView.translatesAutoresizingMaskIntoConstraints = NO;
112 [NSLayoutConstraint
113 activateConstraints:ui_util::CreateSameConstraints(self, effectView)];
114 }
115
116 UIView* actionsView = [self newActionsView];
117 [self initializeOpenCopiedURLSectionUsingTopAnchor:actionsView.bottomAnchor];
118 }
119
120 - (UIView*)newActionsView {
121 // Views added to different effect views need to be constrained to each other.
122 // This is made easier by putting all the constraints into an array and
marq (ping after 24h) 2017/05/09 16:01:43 Activating constraints all at once is *somewhat* m
lody 2017/05/09 16:17:57 As seen offline, this is functionally necessary. P
marq (ping after 24h) 2017/05/10 10:48:42 My rough suggestion: "The vibrancy effects requir
lody 2017/05/10 12:54:02 perfect! thanks.
123 // activating them all at once after adding all the views.
124 NSMutableArray<NSLayoutConstraint*>* constraintArray = [NSMutableArray array];
marq (ping after 24h) 2017/05/10 10:48:42 Rename to 'constraints' (prefer not to include typ
lody 2017/05/10 12:54:02 Done.
125
126 UIStackView* actionRow = [[UIStackView alloc] initWithArrangedSubviews:@[
127 [self newActionViewWithTitle:@"New Search"
128 imageName:@"quick_action_search"
129 actionSelector:@selector(openSearch:)
130 constraintArray:constraintArray],
131 [self newActionViewWithTitle:@"Incognito Search"
132 imageName:@"quick_action_incognito_search"
133 actionSelector:@selector(openIncognito:)
134 constraintArray:constraintArray],
135 [self newActionViewWithTitle:@"Voice Search"
136 imageName:@"quick_action_voice_search"
137 actionSelector:@selector(openVoice:)
138 constraintArray:constraintArray],
139 [self newActionViewWithTitle:@"Scan QR/Bar Code"
140 imageName:@"quick_action_camera_search"
141 actionSelector:@selector(openQRCode:)
142 constraintArray:constraintArray],
143 ]];
144
145 actionRow.axis = UILayoutConstraintAxisHorizontal;
146 actionRow.alignment = UIStackViewAlignmentTop;
147 actionRow.distribution = UIStackViewDistributionFillEqually;
148 actionRow.spacing = kIconSpacing;
149 actionRow.layoutMargins =
150 UIEdgeInsetsMake(0, kContentMargin, 0, kContentMargin);
151 actionRow.layoutMarginsRelativeArrangement = YES;
152 actionRow.translatesAutoresizingMaskIntoConstraints = NO;
153
154 [self.secondaryEffectView.contentView addSubview:actionRow];
155
156 // These constraints stretch the action row to the full width of the widget.
157 // Their priority is < UILayoutPriorityRequired so that they can break when
158 // the view is larger than kMaxContentSize.
159 NSLayoutConstraint* actionsLeftConstraint = [actionRow.leftAnchor
160 constraintEqualToAnchor:self.secondaryEffectView.leftAnchor];
161 actionsLeftConstraint.priority = UILayoutPriorityDefaultHigh;
162
163 NSLayoutConstraint* actionsRightConstraint = [actionRow.rightAnchor
164 constraintEqualToAnchor:self.secondaryEffectView.rightAnchor];
165 actionsRightConstraint.priority = UILayoutPriorityDefaultHigh;
166
167 // This constraint sets the top alignment for the action row. Its priority is
168 // < UILayoutPriorityRequired so that it can break in favor of the
169 // centerYAnchor rule (on the next line) when the copiedURL section is hidden.
170 NSLayoutConstraint* actionsTopConstraint = [actionRow.topAnchor
171 constraintEqualToAnchor:self.secondaryEffectView.topAnchor
172 constant:kContentMargin];
173 actionsTopConstraint.priority = UILayoutPriorityDefaultHigh;
174
175 self.hiddenCopiedURLConstraints = @[ [actionRow.centerYAnchor
176 constraintEqualToAnchor:self.secondaryEffectView.centerYAnchor] ];
177
178 [constraintArray addObjectsFromArray:@[
179 [actionRow.centerXAnchor
180 constraintEqualToAnchor:self.secondaryEffectView.centerXAnchor],
181 [actionRow.widthAnchor constraintLessThanOrEqualToConstant:kMaxContentSize],
182 actionsLeftConstraint,
183 actionsRightConstraint,
184 actionsTopConstraint,
185 ]];
186
187 [NSLayoutConstraint activateConstraints:constraintArray];
188
189 return actionRow;
190 }
191
192 - (UIView*)newActionViewWithTitle:(NSString*)title
193 imageName:(NSString*)imageName
194 actionSelector:(SEL)actionSelector
195 constraintArray:
196 (NSMutableArray<NSLayoutConstraint*>*)constraintArray {
197 UIView* circleView = [[UIView alloc] initWithFrame:CGRectZero];
198 circleView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.05];
199 circleView.layer.cornerRadius = kActionButtonSize / 2;
200
201 [constraintArray addObjectsFromArray:@[
202 [circleView.widthAnchor constraintEqualToConstant:kActionButtonSize],
203 [circleView.heightAnchor constraintEqualToConstant:kActionButtonSize]
204 ]];
205
206 UILabel* labelView = [[UILabel alloc] initWithFrame:CGRectZero];
207 labelView.text = title;
208 labelView.numberOfLines = 0;
209 labelView.textAlignment = NSTextAlignmentCenter;
210 labelView.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
211 [labelView
212 setContentCompressionResistancePriority:UILayoutPriorityRequired
213 forAxis:UILayoutConstraintAxisVertical];
214
215 UIStackView* stack =
216 [[UIStackView alloc] initWithArrangedSubviews:@[ circleView, labelView ]];
217 stack.axis = UILayoutConstraintAxisVertical;
218 stack.spacing = kIconSpacing;
219 stack.alignment = UIStackViewAlignmentCenter;
220 stack.translatesAutoresizingMaskIntoConstraints = NO;
221
222 // A transparent button constrained to the same size as the stack is added to
223 // handle taps on the stack view.
224 UIButton* actionButton = [[UIButton alloc] initWithFrame:CGRectZero];
225 actionButton.backgroundColor = [UIColor clearColor];
226 [actionButton addTarget:self.target
227 action:actionSelector
228 forControlEvents:UIControlEventTouchUpInside];
229 actionButton.translatesAutoresizingMaskIntoConstraints = NO;
230 [self addSubview:actionButton];
231 [constraintArray
232 addObjectsFromArray:ui_util::CreateSameConstraints(actionButton, stack)];
233
234 UIImage* iconImage = [UIImage imageNamed:imageName];
235 UIImageView* icon = [[UIImageView alloc] initWithImage:iconImage];
236 icon.translatesAutoresizingMaskIntoConstraints = NO;
237
238 [constraintArray addObjectsFromArray:@[
239 [icon.widthAnchor constraintEqualToConstant:kIconSize],
240 [icon.heightAnchor constraintEqualToConstant:kIconSize],
241 [icon.centerXAnchor constraintEqualToAnchor:circleView.centerXAnchor],
242 [icon.centerYAnchor constraintEqualToAnchor:circleView.centerYAnchor],
243 ]];
244 [self.primaryEffectView.contentView addSubview:icon];
245
246 return stack;
247 }
248
249 - (void)initializeOpenCopiedURLSectionUsingTopAnchor:
250 (NSLayoutAnchor*)topAnchor {
251 self.hairlineView = [[UIView alloc] initWithFrame:CGRectZero];
252 self.hairlineView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.05];
253 self.hairlineView.translatesAutoresizingMaskIntoConstraints = NO;
254 [self.secondaryEffectView.contentView addSubview:self.hairlineView];
255
256 self.copiedButtonView = [[UIButton alloc] initWithFrame:CGRectZero];
257 self.copiedButtonView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.05];
258 self.copiedButtonView.layer.cornerRadius = 5;
259 self.copiedButtonView.translatesAutoresizingMaskIntoConstraints = NO;
260 [self.secondaryEffectView.contentView addSubview:self.copiedButtonView];
261 [self.copiedButtonView addTarget:self.target
262 action:@selector(openCopiedURL:)
263 forControlEvents:UIControlEventTouchUpInside];
264
265 self.openCopiedURLTitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
266 self.openCopiedURLTitleLabel.textAlignment = NSTextAlignmentCenter;
267 self.openCopiedURLTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
268 self.openCopiedURLTitleLabel.font =
269 [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
270 [self.primaryEffectView.contentView addSubview:self.openCopiedURLTitleLabel];
271
272 self.copiedURLLabel = [[UILabel alloc] initWithFrame:CGRectZero];
273 self.copiedURLLabel.textAlignment = NSTextAlignmentCenter;
274 self.copiedURLLabel.font =
275 [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
276 self.copiedURLLabel.translatesAutoresizingMaskIntoConstraints = NO;
277 [self.secondaryEffectView.contentView addSubview:self.copiedURLLabel];
278
279 self.visibleCopiedURLConstraints = @[
280 [self.hairlineView.topAnchor constraintEqualToAnchor:topAnchor
281 constant:kContentMargin],
282 [self.hairlineView.leftAnchor
283 constraintEqualToAnchor:self.secondaryEffectView.leftAnchor],
284 [self.hairlineView.rightAnchor
285 constraintEqualToAnchor:self.secondaryEffectView.rightAnchor],
286 [self.hairlineView.heightAnchor constraintEqualToConstant:0.5],
287
288 [self.copiedButtonView.centerXAnchor
289 constraintEqualToAnchor:self.secondaryEffectView.centerXAnchor],
290 [self.copiedButtonView.widthAnchor
291 constraintEqualToAnchor:self.secondaryEffectView.widthAnchor
292 constant:-2 * kContentMargin],
293 [self.copiedButtonView.topAnchor
294 constraintEqualToAnchor:self.hairlineView.bottomAnchor
295 constant:12],
296 [self.copiedButtonView.bottomAnchor
297 constraintEqualToAnchor:self.secondaryEffectView.bottomAnchor
298 constant:-kContentMargin],
299
300 [self.openCopiedURLTitleLabel.centerXAnchor
301 constraintEqualToAnchor:self.primaryEffectView.centerXAnchor],
302 [self.openCopiedURLTitleLabel.topAnchor
303 constraintEqualToAnchor:self.copiedButtonView.topAnchor
304 constant:kURLButtonMargin],
305 [self.openCopiedURLTitleLabel.widthAnchor
306 constraintEqualToAnchor:self.copiedButtonView.widthAnchor
307 constant:-kContentMargin * 2],
308
309 [self.copiedURLLabel.centerXAnchor
310 constraintEqualToAnchor:self.primaryEffectView.centerXAnchor],
311 [self.copiedURLLabel.topAnchor
312 constraintEqualToAnchor:self.openCopiedURLTitleLabel.bottomAnchor],
313 [self.copiedURLLabel.widthAnchor
314 constraintEqualToAnchor:self.openCopiedURLTitleLabel.widthAnchor],
315 [self.copiedURLLabel.bottomAnchor
316 constraintEqualToAnchor:self.copiedButtonView.bottomAnchor
317 constant:-kURLButtonMargin],
318 ];
319 }
320
321 - (void)addTapAction:(SEL)action toView:(UIView*)view {
58 UIGestureRecognizer* tapRecognizer = 322 UIGestureRecognizer* tapRecognizer =
59 [[UITapGestureRecognizer alloc] initWithTarget:_target 323 [[UITapGestureRecognizer alloc] initWithTarget:self.target action:action];
60 action:@selector(openSearch:)]; 324 [view addGestureRecognizer:tapRecognizer];
61 325 }
62 [fakebox addGestureRecognizer:tapRecognizer]; 326
63 [self addSubview:fakebox]; 327 - (void)updateCopiedURLUI {
64 328 // If the copiedURL section is not visible, hide all the copiedURL section
65 UIView* cursor = [[UIView alloc] initWithFrame:CGRectZero]; 329 // views and activate the correct constraint set. If it is visible, show the
66 self.cursor = cursor; 330 // views in function of whether there is or not a copied URL to show.
67 self.cursor.backgroundColor = [UIColor blueColor]; 331
68 [fakebox addSubview:self.cursor]; 332 if (!self.copiedURLVisible) {
69 333 self.copiedURLLabel.hidden = YES;
70 [fakebox setTranslatesAutoresizingMaskIntoConstraints:NO]; 334 self.openCopiedURLTitleLabel.hidden = YES;
71 [self.cursor setTranslatesAutoresizingMaskIntoConstraints:NO]; 335 self.hairlineView.hidden = YES;
72 [NSLayoutConstraint activateConstraints:@[ 336 self.copiedButtonView.hidden = YES;
73 [[fakebox leadingAnchor] constraintEqualToAnchor:self.leadingAnchor 337 [NSLayoutConstraint deactivateConstraints:self.visibleCopiedURLConstraints];
74 constant:kFakeboxHorizontalPadding], 338 [NSLayoutConstraint activateConstraints:self.hiddenCopiedURLConstraints];
75 [[fakebox trailingAnchor] 339 return;
76 constraintEqualToAnchor:self.trailingAnchor 340 }
77 constant:-kFakeboxHorizontalPadding], 341
78 [[fakebox topAnchor] constraintEqualToAnchor:self.topAnchor 342 self.copiedURLLabel.hidden = NO;
79 constant:kFakeboxVerticalPadding], 343 self.openCopiedURLTitleLabel.hidden = NO;
80 [[fakebox heightAnchor] 344
81 constraintEqualToConstant:kCursorHeight + 2 * kCursorVerticalPadding], 345 [NSLayoutConstraint deactivateConstraints:self.hiddenCopiedURLConstraints];
82 346 [NSLayoutConstraint activateConstraints:self.visibleCopiedURLConstraints];
83 [[self.cursor widthAnchor] constraintEqualToConstant:kCursorWidth], 347
84 [[self.cursor leadingAnchor] 348 if (self.copiedURLString) {
85 constraintEqualToAnchor:fakebox.leadingAnchor 349 self.copiedButtonView.hidden = NO;
86 constant:kCursorHorizontalPadding], 350 self.hairlineView.hidden = YES;
87 [[self.cursor heightAnchor] constraintEqualToConstant:kCursorHeight], 351 self.copiedURLLabel.text = self.copiedURLString;
88 [[self.cursor centerYAnchor] constraintEqualToAnchor:fakebox.centerYAnchor] 352 self.openCopiedURLTitleLabel.alpha = 1;
89 ]]; 353 self.openCopiedURLTitleLabel.text = @"Open Copied Link";
90 354 self.copiedURLLabel.alpha = 1;
91 [UIView animateWithDuration:0.3 355 return;
92 delay:0.0 356 }
93 options:UIViewAnimationOptionRepeat | 357
94 UIViewAnimationOptionAutoreverse 358 self.copiedButtonView.hidden = YES;
95 animations:^{ 359 self.hairlineView.hidden = NO;
96 self.cursor.alpha = 0.0f; 360 self.copiedURLLabel.text = @"Links you copy will appear here.";
97 } 361 self.openCopiedURLTitleLabel.alpha = 0.5;
98 completion:nil]; 362 self.openCopiedURLTitleLabel.text = @"No Copied Link";
99 } 363 self.copiedURLLabel.alpha = 0.5;
100 364 }
101 - (void)updateCopiedURL:(NSString*)copiedURL {
102 self.copiedURL = copiedURL;
103 self.copiedURLLabel.text = copiedURL;
104 }
105
106 @end 365 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698