You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/SignalMessaging/Views/OWSSearchBar.m

332 lines
9.3 KiB
Objective-C

//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSSearchBar.h"
#import "OWSTextField.h"
#import "Theme.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/UIImage+OWS.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSSearchBar () <UITextFieldDelegate, OWSTextFieldDelegate>
@property (nonatomic) OWSTextField *textField;
@property (nonatomic) UIButton *cancelButton;
@property (nonatomic) CAShapeLayer *pillboxLayer;
@property (nonatomic) OWSLayerView *pillboxView;
@property (nonatomic) UIButton *clearButton;
@property (nonatomic) UIImageView *searchIconView;
@property (nonatomic) UIStackView *contentStackView;
@end
#pragma mark -
@implementation OWSSearchBar
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self ows_configure];
}
return self;
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self ows_configure];
}
return self;
}
- (CGFloat)contentHMargin
{
return 8.f;
}
- (CGFloat)contentVMargin
{
return 10.f;
}
- (CGFloat)pillboxHMargin
{
return 8.f;
}
- (CGFloat)pillboxVMargin
{
return 7.f;
}
- (CGFloat)contentSpacing
{
return 12.f;
}
- (CGFloat)pillboxSpacing
{
return 8.f;
}
+ (CGFloat)pillboxRadius
{
return 10.f;
}
- (CGSize)sizeThatFits:(CGSize)size
{
// All that matters is the height.
CGSize result = [self.textField sizeThatFits:CGSizeZero];
result.width += self.pillboxHMargin * 2;
result.height += self.pillboxVMargin * 2;
result.width += self.contentHMargin * 2;
result.height += self.contentVMargin * 2;
return result;
}
- (void)ows_configure
{
OWSAssert(!self.searchIconView);
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
UIImage *_Nullable searchIcon = [UIImage imageNamed:@"searchbar_search"];
searchIcon = [searchIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
OWSAssert(searchIcon);
self.searchIconView = [[UIImageView alloc] initWithImage:searchIcon];
[self.searchIconView setContentHuggingHigh];
[self.searchIconView setCompressionResistanceHigh];
UIImage *_Nullable clearIcon = [UIImage imageNamed:@"searchbar_clear"];
clearIcon = [clearIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
OWSAssert(clearIcon);
self.clearButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.clearButton setImage:clearIcon forState:UIControlStateNormal];
[self.clearButton addTarget:self action:@selector(clearButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.clearButton setContentHuggingHigh];
[self.clearButton setCompressionResistanceHigh];
self.textField = [OWSTextField new];
self.textField.font = [UIFont ows_dynamicTypeBodyFont];
self.textField.delegate = self;
self.textField.ows_delegate = self;
self.textField.returnKeyType = UIReturnKeySearch;
self.textField.enablesReturnKeyAutomatically = YES;
[self.textField addTarget:self action:@selector(textDidChange:) forControlEvents:UIControlEventEditingChanged];
[self.textField addTarget:self
action:@selector(textFieldReturnWasPressed:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self.textField setContentHuggingVerticalLow];
[self.textField setContentHuggingVerticalHigh];
[self.textField setCompressionResistanceHorizontalLow];
[self.textField setCompressionResistanceVerticalHigh];
UIStackView *pillboxStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
self.searchIconView,
self.textField,
self.clearButton,
]];
pillboxStackView.spacing = self.pillboxSpacing;
pillboxStackView.axis = UILayoutConstraintAxisHorizontal;
pillboxStackView.alignment = UIStackViewAlignmentCenter;
pillboxStackView.layoutMargins
= UIEdgeInsetsMake(self.pillboxVMargin, self.pillboxHMargin, self.pillboxVMargin, self.pillboxHMargin);
pillboxStackView.layoutMarginsRelativeArrangement = YES;
self.cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.cancelButton setTitle:CommonStrings.cancelButton forState:UIControlStateNormal];
self.cancelButton.titleLabel.font = UIFont.ows_dynamicTypeBodyFont;
[self.cancelButton setContentHuggingHigh];
[self.cancelButton setCompressionResistanceHigh];
[self.cancelButton addTarget:self
action:@selector(cancelButtonPressed)
forControlEvents:UIControlEventTouchUpInside];
self.contentStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
pillboxStackView,
self.cancelButton,
]];
self.contentStackView.spacing = self.contentSpacing;
self.contentStackView.axis = UILayoutConstraintAxisHorizontal;
self.contentStackView.alignment = UIStackViewAlignmentCenter;
[self addSubview:self.contentStackView];
[self.contentStackView autoPinWidthToSuperviewWithMargin:self.contentHMargin];
[self.contentStackView autoPinHeightToSuperviewWithMargin:self.contentVMargin];
CAShapeLayer *pillboxLayer = [CAShapeLayer new];
self.pillboxLayer = pillboxLayer;
self.pillboxView = [[OWSLayerView alloc]
initWithFrame:CGRectZero
layoutCallback:^(UIView *layerView) {
pillboxLayer.path =
[UIBezierPath bezierPathWithRoundedRect:layerView.bounds cornerRadius:OWSSearchBar.pillboxRadius]
.CGPath;
}];
self.pillboxView.userInteractionEnabled = NO;
[self.pillboxView.layer addSublayer:pillboxLayer];
[pillboxStackView addSubview:self.pillboxView];
[self.pillboxView autoPinEdgesToSuperviewEdges];
[self.pillboxView setContentHuggingLow];
[self.pillboxView setCompressionResistanceLow];
self.pillboxView.layer.zPosition = -1;
self.userInteractionEnabled = YES;
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapSearchBar:)]];
[self ows_applyTheme];
[self updateState];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(themeDidChange:)
name:ThemeDidChangeNotification
object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)ows_applyTheme
{
OWSAssertIsOnMainThread();
self.searchIconView.tintColor = Theme.placeholderColor;
self.clearButton.tintColor = Theme.placeholderColor;
self.pillboxLayer.fillColor = Theme.offBackgroundColor.CGColor;
self.textField.textColor = Theme.primaryColor;
if (self.placeholder.length > 0) {
self.textField.attributedPlaceholder =
[[NSAttributedString alloc] initWithString:self.placeholder
attributes:@{
NSForegroundColorAttributeName : Theme.placeholderColor,
}];
}
[self.cancelButton setTitleColor:Theme.primaryColor forState:UIControlStateNormal];
}
- (void)updateState
{
self.cancelButton.hidden = (!self.textField.isFirstResponder && self.textField.text.length < 1);
self.clearButton.hidden = self.textField.text.length < 1;
}
- (void)setPlaceholder:(nullable NSString *)placeholder
{
self.textField.attributedPlaceholder =
[[NSAttributedString alloc] initWithString:placeholder
attributes:@{
NSForegroundColorAttributeName : Theme.placeholderColor,
}];
}
- (nullable NSString *)placeholder
{
return self.textField.placeholder;
}
- (void)setText:(nullable NSString *)text
{
self.textField.text = text;
[self updateState];
}
- (nullable NSString *)text
{
return self.textField.text;
}
- (BOOL)becomeFirstResponder
{
return [self.textField becomeFirstResponder];
}
- (BOOL)resignFirstResponder
{
return [self.textField resignFirstResponder];
}
#pragma mark - OWSTextFieldDelegate
- (void)textFieldDidBecomeFirstResponder:(OWSTextField *)textField
{
[self updateState];
if ([self.delegate respondsToSelector:@selector(searchBarDidBeginEditing:)]) {
[self.delegate searchBarDidBeginEditing:self];
}
}
- (void)textFieldDidResignFirstResponder:(OWSTextField *)textField
{
[self updateState];
}
#pragma mark - Events
- (void)themeDidChange:(NSNotification *)notification
{
OWSAssertIsOnMainThread();
[self ows_applyTheme];
}
- (void)textDidChange:(id)sender
{
[self updateState];
[self.delegate searchBar:self textDidChange:self.text];
}
- (void)textFieldReturnWasPressed:(id)sender
{
[self.textField resignFirstResponder];
[self updateState];
[self.delegate searchBar:self returnWasPressed:self.text];
}
- (void)cancelButtonPressed
{
self.text = nil;
[self updateState];
[self.delegate searchBar:self textDidChange:self.text];
}
- (void)clearButtonPressed
{
self.text = nil;
[self updateState];
[self.delegate searchBar:self textDidChange:self.text];
}
- (void)didTapSearchBar:(UIGestureRecognizer *)sender
{
[self becomeFirstResponder];
}
@end
NS_ASSUME_NONNULL_END