Skip to content

Remove constants and expose styling #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLTokenInputView.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#

s.name = "CLTokenInputView"
s.version = "2.4.0"
s.version = "2.5.0"
s.summary = "A replica of iOS's native contact bubbles UI."

s.description = <<-DESC
Expand Down
4 changes: 4 additions & 0 deletions CLTokenInputView.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
65B16BC218BC26C9003AA819 /* CLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 65B16BC118BC26C9003AA819 /* CLToken.m */; };
65B16BC318BC26C9003AA819 /* CLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 65B16BC118BC26C9003AA819 /* CLToken.m */; };
65E86F821C407E3A0029E724 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 65E86F811C407E3A0029E724 /* LaunchScreen.storyboard */; };
BAC0AB601E0075FD00DFE9C4 /* CLTokenInputView.podspec in Resources */ = {isa = PBXBuildFile; fileRef = BAC0AB5F1E0075FD00DFE9C4 /* CLTokenInputView.podspec */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -73,6 +74,7 @@
65B16BC018BC26C9003AA819 /* CLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLToken.h; path = CLTokenInputView/CLToken.h; sourceTree = "<group>"; };
65B16BC118BC26C9003AA819 /* CLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CLToken.m; path = CLTokenInputView/CLToken.m; sourceTree = "<group>"; };
65E86F811C407E3A0029E724 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
BAC0AB5F1E0075FD00DFE9C4 /* CLTokenInputView.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CLTokenInputView.podspec; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -103,6 +105,7 @@
isa = PBXGroup;
children = (
65B16B8318BC16F6003AA819 /* CLTokenInputView */,
BAC0AB5F1E0075FD00DFE9C4 /* CLTokenInputView.podspec */,
65B16B9C18BC16F7003AA819 /* CLTokenInputViewTests */,
65B16B7C18BC16F6003AA819 /* Frameworks */,
65B16B7B18BC16F6003AA819 /* Products */,
Expand Down Expand Up @@ -268,6 +271,7 @@
65B16B8818BC16F6003AA819 /* InfoPlist.strings in Resources */,
65E86F821C407E3A0029E724 /* LaunchScreen.storyboard in Resources */,
65B16B9018BC16F6003AA819 /* Images.xcassets in Resources */,
BAC0AB601E0075FD00DFE9C4 /* CLTokenInputView.podspec in Resources */,
65B16BBA18BC1826003AA819 /* CLTokenInputViewController.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
13 changes: 13 additions & 0 deletions CLTokenInputView/CLTokenInputView/CLTokenInputView.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,27 @@ NS_ASSUME_NONNULL_BEGIN
@property (strong, nonatomic, nullable) UIView *fieldView;
/** Option text which can be displayed before the first line (e.g. "To:") */
@property (copy, nonatomic, nullable) IBInspectable NSString *fieldName;
@property (copy, nonatomic, nullable) IBInspectable NSAttributedString *attributedFieldName;
/** Color of optional */
@property (strong, nonatomic, nullable) IBInspectable UIColor *fieldColor;
@property (copy, nonatomic, nullable) IBInspectable NSString *placeholderText;
@property (copy, nonatomic, nullable) IBInspectable NSAttributedString *attributedPlaceholder;
@property (strong, nonatomic, nullable) UIView *accessoryView;
@property (assign, nonatomic) IBInspectable UIKeyboardType keyboardType;
@property (assign, nonatomic) IBInspectable UITextAutocapitalizationType autocapitalizationType;
@property (assign, nonatomic) IBInspectable UITextAutocorrectionType autocorrectionType;
@property (assign, nonatomic) IBInspectable UIKeyboardAppearance keyboardAppearance;
@property (assign, nonatomic) IBInspectable UIEdgeInsets padding;
@property (assign, nonatomic) IBInspectable UIEdgeInsets fieldPadding;
@property (assign, nonatomic) IBInspectable UIEdgeInsets tokenPadding;
@property (assign, nonatomic) IBInspectable CGFloat standardRowHeight;
@property (assign, nonatomic) IBInspectable CGFloat textFieldHSpace;
@property (assign, nonatomic) IBInspectable CGFloat linePadding;
@property (assign, nonatomic) IBInspectable CGFloat minimumTextFieldWidth;

@property (copy, nonatomic) IBInspectable NSDictionary<NSString *,id> *defaultTextAttributes;
@property (copy, nonatomic) IBInspectable NSDictionary<NSString *,id> *selectedTextAttributes;
@property (copy, nonatomic) IBInspectable NSDictionary<NSString *,id> *placeholderTextAttributes;
/**
* Optional additional characters to trigger the tokenization process (and call the delegate
* with `tokenInputView:tokenForText:`
Expand Down
163 changes: 126 additions & 37 deletions CLTokenInputView/CLTokenInputView/CLTokenInputView.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@
#import "CLTokenView.h"

static CGFloat const HSPACE = 0.0;
static CGFloat const TEXT_FIELD_HSPACE = 4.0; // Note: Same as CLTokenView.PADDING_X
static CGFloat const VSPACE = 4.0;
static CGFloat const MINIMUM_TEXTFIELD_WIDTH = 56.0;
static CGFloat const PADDING_TOP = 10.0;
static CGFloat const PADDING_BOTTOM = 10.0;
static CGFloat const PADDING_LEFT = 8.0;
static CGFloat const PADDING_RIGHT = 16.0;
static CGFloat const STANDARD_ROW_HEIGHT = 25.0;

static CGFloat const FIELD_MARGIN_X = 4.0; // Note: Same as CLTokenView.PADDING_X

@interface CLTokenInputView () <CLBackspaceDetectingTextFieldDelegate, CLTokenViewDelegate>

Expand All @@ -40,6 +30,13 @@ @implementation CLTokenInputView

- (void)commonInit
{
_padding = UIEdgeInsetsMake(10.0, 8.0, 10.0, 16.0);
_fieldPadding = UIEdgeInsetsMake(0.0, 4.0, 0.0, 4.0);
_standardRowHeight = 25.0;
_textFieldHSpace = 4.0;
_linePadding = 4.0;
_minimumTextFieldWidth = 56.0;
_tokenPadding = UIEdgeInsetsMake(2.0, 4.0, 2.0, 4.0);
self.textField = [[CLBackspaceDetectingTextField alloc] initWithFrame:self.bounds];
self.textField.backgroundColor = [UIColor clearColor];
self.textField.keyboardType = UIKeyboardTypeEmailAddress;
Expand All @@ -66,7 +63,6 @@ - (void)commonInit
[self addSubview:self.fieldLabel];
self.fieldLabel.hidden = YES;

self.intrinsicContentHeight = STANDARD_ROW_HEIGHT;
[self repositionViews];
}

Expand All @@ -90,7 +86,7 @@ - (id)initWithCoder:(NSCoder *)aDecoder

- (CGSize)intrinsicContentSize
{
return CGSizeMake(UIViewNoIntrinsicMetric, MAX(45, self.intrinsicContentHeight));
return CGSizeMake(UIViewNoIntrinsicMetric, self.intrinsicContentHeight);
}


Expand All @@ -115,6 +111,11 @@ - (void)addToken:(CLToken *)token

[self.tokens addObject:token];
CLTokenView *tokenView = [[CLTokenView alloc] initWithToken:token font:self.textField.font];
tokenView.padding = _tokenPadding;
tokenView.defaultTextAttributes = self.defaultTextAttributes;
tokenView.selectedTextAttributes = self.selectedTextAttributes;
tokenView.inputKeyboardAppearance = self.keyboardAppearance;
tokenView.inputKeyboardType = self.keyboardType;
if ([self respondsToSelector:@selector(tintColor)]) {
tokenView.tintColor = self.tintColor;
}
Expand Down Expand Up @@ -187,41 +188,42 @@ - (CLToken *)tokenizeTextfieldText

- (void)repositionViews
{
self.intrinsicContentHeight = self.standardRowHeight;
CGRect bounds = self.bounds;
CGFloat rightBoundary = CGRectGetWidth(bounds) - PADDING_RIGHT;
CGFloat rightBoundary = CGRectGetWidth(bounds) - self.padding.right;
CGFloat firstLineRightBoundary = rightBoundary;

CGFloat curX = PADDING_LEFT;
CGFloat curY = PADDING_TOP;
CGFloat totalHeight = STANDARD_ROW_HEIGHT;
CGFloat curX = self.padding.left;
CGFloat curY = self.padding.top;
CGFloat totalHeight = self.standardRowHeight;
BOOL isOnFirstLine = YES;

// Position field view (if set)
if (self.fieldView) {
CGRect fieldViewRect = self.fieldView.frame;
fieldViewRect.origin.x = curX + FIELD_MARGIN_X;
fieldViewRect.origin.y = curY + ((STANDARD_ROW_HEIGHT - CGRectGetHeight(fieldViewRect))/2.0);
fieldViewRect.origin.x = curX + self.fieldPadding.left;
fieldViewRect.origin.y = curY + ((self.standardRowHeight - CGRectGetHeight(fieldViewRect))/2.0);
self.fieldView.frame = fieldViewRect;

curX = CGRectGetMaxX(fieldViewRect) + FIELD_MARGIN_X;
curX = CGRectGetMaxX(fieldViewRect) + self.fieldPadding.right;
}

// Position field label (if field name is set)
if (!self.fieldLabel.hidden) {
CGSize labelSize = self.fieldLabel.intrinsicContentSize;
CGRect fieldLabelRect = CGRectZero;
fieldLabelRect.size = labelSize;
fieldLabelRect.origin.x = curX + FIELD_MARGIN_X;
fieldLabelRect.origin.y = curY + ((STANDARD_ROW_HEIGHT-CGRectGetHeight(fieldLabelRect))/2.0);
fieldLabelRect.origin.x = curX + self.fieldPadding.left;
fieldLabelRect.origin.y = curY + ((self.standardRowHeight-CGRectGetHeight(fieldLabelRect))/2.0);
self.fieldLabel.frame = fieldLabelRect;

curX = CGRectGetMaxX(fieldLabelRect) + FIELD_MARGIN_X;
curX = CGRectGetMaxX(fieldLabelRect) + self.fieldPadding.right;
}

// Position accessory view (if set)
if (self.accessoryView) {
CGRect accessoryRect = self.accessoryView.frame;
accessoryRect.origin.x = CGRectGetWidth(bounds) - PADDING_RIGHT - CGRectGetWidth(accessoryRect);
accessoryRect.origin.x = CGRectGetWidth(bounds) - self.padding.right - CGRectGetWidth(accessoryRect);
accessoryRect.origin.y = curY;
self.accessoryView.frame = accessoryRect;

Expand All @@ -236,33 +238,33 @@ - (void)repositionViews
CGFloat tokenBoundary = isOnFirstLine ? firstLineRightBoundary : rightBoundary;
if (curX + CGRectGetWidth(tokenRect) > tokenBoundary) {
// Need a new line
curX = PADDING_LEFT;
curY += STANDARD_ROW_HEIGHT+VSPACE;
totalHeight += STANDARD_ROW_HEIGHT;
curX = self.padding.left;
curY += self.standardRowHeight+self.linePadding;
totalHeight += self.standardRowHeight;
isOnFirstLine = NO;
}

tokenRect.origin.x = curX;
// Center our tokenView vertically within STANDARD_ROW_HEIGHT
tokenRect.origin.y = curY + ((STANDARD_ROW_HEIGHT-CGRectGetHeight(tokenRect))/2.0);
// Center our tokenView vertically within self.standardRowHeight
tokenRect.origin.y = curY + ((self.standardRowHeight-CGRectGetHeight(tokenRect))/2.0);
tokenView.frame = tokenRect;

curX = CGRectGetMaxX(tokenRect) + HSPACE;
}

// Always indent textfield by a little bit
curX += TEXT_FIELD_HSPACE;
curX += self.textFieldHSpace;
CGFloat textBoundary = isOnFirstLine ? firstLineRightBoundary : rightBoundary;
CGFloat availableWidthForTextField = textBoundary - curX;
if (availableWidthForTextField < MINIMUM_TEXTFIELD_WIDTH) {
if (availableWidthForTextField < self.minimumTextFieldWidth) {
isOnFirstLine = NO;
// If in the future we add more UI elements below the tokens,
// isOnFirstLine will be useful, and this calculation is important.
// So leaving it set here, and marking the warning to ignore it
#pragma unused(isOnFirstLine)
curX = PADDING_LEFT + TEXT_FIELD_HSPACE;
curY += STANDARD_ROW_HEIGHT+VSPACE;
totalHeight += STANDARD_ROW_HEIGHT;
curX = self.padding.left + self.textFieldHSpace;
curY += self.standardRowHeight+self.linePadding;
totalHeight += self.standardRowHeight;
// Adjust the width
availableWidthForTextField = rightBoundary - curX;
}
Expand All @@ -271,11 +273,11 @@ - (void)repositionViews
textFieldRect.origin.x = curX;
textFieldRect.origin.y = curY + self.additionalTextFieldYOffset;
textFieldRect.size.width = availableWidthForTextField;
textFieldRect.size.height = STANDARD_ROW_HEIGHT;
textFieldRect.size.height = self.standardRowHeight;
self.textField.frame = textFieldRect;

CGFloat oldContentHeight = self.intrinsicContentHeight;
self.intrinsicContentHeight = MAX(totalHeight, CGRectGetMaxY(textFieldRect)+PADDING_BOTTOM);
self.intrinsicContentHeight = MAX(totalHeight, CGRectGetMaxY(textFieldRect)+self.padding.bottom);
[self invalidateIntrinsicContentSize];

if (oldContentHeight != self.intrinsicContentHeight) {
Expand Down Expand Up @@ -405,8 +407,8 @@ - (void)setKeyboardAppearance:(UIKeyboardAppearance)keyboardAppearance

- (CGFloat)textFieldDisplayOffset
{
// Essentially the textfield's y with PADDING_TOP
return CGRectGetMinY(self.textField.frame) - PADDING_TOP;
// Essentially the textfield's y with self.padding.top
return CGRectGetMinY(self.textField.frame) - self.padding.top;
}


Expand Down Expand Up @@ -494,6 +496,60 @@ - (void)endEditing

#pragma mark - (Optional Views)

- (void)setPadding:(UIEdgeInsets)padding
{
if (UIEdgeInsetsEqualToEdgeInsets(_padding, padding)) {
return;
}
_padding = padding;
[self repositionViews];
}

- (void)setFieldPadding:(UIEdgeInsets)fieldPadding
{
if (UIEdgeInsetsEqualToEdgeInsets(_fieldPadding, fieldPadding)) {
return;
}
_fieldPadding = fieldPadding;
[self repositionViews];
}

- (void)setStandardRowHeight:(CGFloat)standardRowHeight
{
if (_standardRowHeight == standardRowHeight) {
return;
}
_standardRowHeight = standardRowHeight;
[self repositionViews];
}

- (void)setTextFieldHSpace:(CGFloat)textFieldHSpace
{
if (_textFieldHSpace == textFieldHSpace) {
return;
}
_textFieldHSpace = textFieldHSpace;
[self repositionViews];
}

- (void)setLinePadding:(CGFloat)linePadding
{
if (_linePadding == linePadding) {
return;
}
_linePadding = linePadding;
[self repositionViews];
}

- (void) setMinimumTextFieldWidth:(CGFloat)minimumTextFieldWidth
{
if (_minimumTextFieldWidth == minimumTextFieldWidth) {
return;
}
_minimumTextFieldWidth = minimumTextFieldWidth;
[self repositionViews];
}

- (void)setFieldName:(NSString *)fieldName
{
if (_fieldName == fieldName) {
Expand All @@ -517,6 +573,39 @@ - (void)setFieldName:(NSString *)fieldName
}
}

- (void)setAttributedFieldName:(NSAttributedString *)attributedFieldName
{
if (_attributedFieldName == attributedFieldName) {
return;
}
NSAttributedString *oldAttributedFieldName = _attributedFieldName;
_attributedFieldName = attributedFieldName;

self.fieldLabel.attributedText = _attributedFieldName;
[self.fieldLabel invalidateIntrinsicContentSize];
BOOL showField = (_attributedFieldName.string.length > 0);
self.fieldLabel.hidden = !showField;
if (showField && !self.fieldLabel.superview) {
[self addSubview:self.fieldLabel];
} else if (!showField && self.fieldLabel.superview) {
[self.fieldLabel removeFromSuperview];
}

if (oldAttributedFieldName == nil || ![oldAttributedFieldName isEqualToAttributedString:attributedFieldName]) {
[self repositionViews];
}
}

- (void)setDefaultTextAttributes:(NSDictionary<NSString *,id> *)defaultTextAttributes
{
self.textField.defaultTextAttributes = defaultTextAttributes;
}

- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder
{
self.textField.attributedPlaceholder = attributedPlaceholder;
}

- (void)setFieldColor:(UIColor *)fieldColor {
_fieldColor = fieldColor;
self.fieldLabel.textColor = _fieldColor;
Expand Down
5 changes: 5 additions & 0 deletions CLTokenInputView/CLTokenInputView/CLTokenView.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
@property (weak, nonatomic, nullable) NSObject <CLTokenViewDelegate> *delegate;
@property (assign, nonatomic) BOOL selected;
@property (assign, nonatomic) BOOL hideUnselectedComma;
@property (assign, nonatomic) UIEdgeInsets padding;
@property (copy, nonatomic) NSDictionary<NSString *,id> *defaultTextAttributes;
@property (copy, nonatomic) NSDictionary<NSString *,id> *selectedTextAttributes;
@property (assign, nonatomic) UIKeyboardAppearance inputKeyboardAppearance;
@property (assign, nonatomic) UIKeyboardType inputKeyboardType;

- (id)initWithToken:(CLToken *)token font:(nullable UIFont *)font;

Expand Down
Loading