今天需要做的界面如下(发段子界面):
1、初始化导航栏
- 点击发段子按钮后push到DSPostWordViewController
- (void)buttonClick:(UIButton *)button
{
[self cancelWithCompletionBlock:^{
if (button.tag == 2) { // 段子
DSPostWordViewController *postVc = [[DSPostWordViewController alloc] init];
DSNavigationController *nav = [[DSNavigationController alloc] initWithRootViewController:postVc];
UIViewController *rootVc = [UIApplication sharedApplication].keyWindow.rootViewController;
[rootVc presentViewController:nav animated:YES completion:nil];
}
}];
}
首先把DSPostWordViewController包装成导航控制器,接着找出rootViewController,通过modal的方法弹出(这里不可以用self,因为self在点击button后就dismiss了)
- 初始化导航栏内容
/**
* 初始化导航栏
*/
- (void)setupNav
{
self.title = @"发表文字";
self.view.backgroundColor = DSThemeColor;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消" style:(UIBarButtonItemStyleDone) target:self action:@selector(cancleItemClick)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"发表" style:(UIBarButtonItemStyleDone) target:self action:@selector(postItemClick)];
self.navigationItem.rightBarButtonItem.enabled = NO;
// 强制刷新
[self.navigationController.navigationBar layoutIfNeeded];
}
UIBarButtonItem *barButtonItem = [UIBarButtonItem appearance];
// normal状态下的item属性
NSMutableDictionary *normalAttris = [NSMutableDictionary dictionary];
normalAttris[NSForegroundColorAttributeName] = [UIColor blackColor];
[barButtonItem setTitleTextAttributes:normalAttris forState:(UIControlStateNormal)];
// disable状态下的item属性
NSMutableDictionary *disableAttris = [NSMutableDictionary dictionary];
disableAttris[NSForegroundColorAttributeName] = [UIColor lightGrayColor];
[barButtonItem setTitleTextAttributes:disableAttris forState:(UIControlStateDisabled)];
一些通用性的设置可以放在自定义导航栏的initialize方法中只设置一次
由于enabled的设置要等显示完毕才会有这个效果,所以可以我们会想到把这句代码放到viewDidAppear方法中去,但是这样又会导致我们先看到黑色,然后看到看到灰色。所以最好的办法就是强制刷新(layoutIfNeeded)
2、实现带占位文字的textView
想实现带占位文字的textView,可以用两种常规的方法,但前提都是自定义textView。
- 第一种是将占位文字给画出来
通过重写drawRect方法,将占位文字给画出来,但是这个方法有个缺点(当我们想要让textView可以滚动时,占位文字并不会和textView一起滚动),所以就衍生了下面一个做法!
- 第二种在textView中添加一个label(这次使用的也是这种方法)
1> 首先在initWithFrame方法中进行一些初始化设置,并且监听文字的改变来判断要不要显示占位文字。
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 垂直方向上永远有滚动效果
self.alwaysBounceVertical = YES;
// 默认字体颜色
self.placeholderColor = [UIColor lightGrayColor];
// 设置字体
self.font = [UIFont systemFontOfSize:15];
// 文字改变通知
[DSNotificationCenter addObserver:self selector:@selector(textDidChange) name:UITextViewTextDidChangeNotification object:nil];
}
return self;
}
/**
* 文本改变监听
*/
- (void)textDidChange
{
self.placeholderLabel.hidden = self.hasText;
}
2> 在layoutSubView方法中更新label的尺寸
/**
* 文本改变监听
*/
- (void)textDidChange
{
self.placeholderLabel.hidden = self.hasText;
}
3> 在外界改变占位文字,占位文字颜色,字体这些属性时,label的尺寸就会发生改变,所以可以在赋值完文字后,调用layoutSubView方法及时更新尺寸
- (void)setPlaceholder:(NSString *)placeholder
{
_placeholder = [placeholder copy];
self.placeholderLabel.text = placeholder;
[self setNeedsLayout];
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
_placeholderColor = placeholderColor;
self.placeholderLabel.textColor = placeholderColor;
[self setNeedsLayout];
}
- (void)setFont:(UIFont *)font
{
[super setFont:font];
self.placeholderLabel.font = font;
[self setNeedsLayout];
}
4> 在外界改变text或attributedText时需要监听占位文字的显示或隐藏
- (void)setText:(NSString *)text
{
[super setText:text];
[self textDidChange];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
[super setAttributedText:attributedText];
[self textDidChange];
}
5> 传递两个公开属性给外界(占位文字、占位文字颜色)
@property (nonatomic, strong) UIColor *placeholderColor;
@property (nonatomic, copy) NSString *placeholder;
3、添加底部的工具条
1> 添加+号按钮
/**
* 创建+号按钮
*/
- (void)awakeFromNib
{
UIButton *addButton = [[UIButton alloc] init];
[addButton addTarget:self action:@selector(addButtonClick) forControlEvents:(UIControlEventTouchUpInside)];
[addButton setImage:[UIImage imageNamed:@"tag_add_icon"] forState:(UIControlStateNormal)];
addButton.size = addButton.currentImage.size;
// addButton.size = [addButton imageForState:(UIControlStateNormal)].size;
// addButton.size = [UIImage imageNamed:@""].size;
[self.topView addSubview:addButton];
self.addButton = addButton;
}
2> 监听按钮点击(跳转到DSAddTagViewController)
/**
* 监听addButton点击
*/
- (void)addButtonClick
{
DSAddTagViewController *addTagVc = [[DSAddTagViewController alloc] init];
UINavigationController *nav = (UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController;
[nav pushViewController:addTagVc animated:YES];
}
跳转至添加标签按钮界面。。。。。。
该界面的实现思路:
- 首先添加一个整体的View
- 添加一个textField
- 输入文字时,显示底部的添加按钮
- 点击添加按钮后,显示一个标签按钮到textField的前面
4、初始化导航栏和整体View
/**
* 初始化导航栏
*/
- (void)setupNav
{
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"添加标签";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"完成" style:(UIBarButtonItemStyleDone) target:self action:@selector(done)];
}
/**
* 初始化整体内容
*/
- (void)setupContentView
{
UIView *contentView = [[UIView alloc] init];
contentView.x = DSTagButtonMargin;
contentView.size = CGSizeMake(DSScreenW - contentView.x * 2, DSScreenH);
contentView.y = 64 + DSTagButtonMargin;
[self.view addSubview:contentView];
self.contentView = contentView;
}
5、添加textField
/**
* 初始化textField
*/
- (void)setupTextFiled
{
__weak typeof(self) weakSelf = self;
DSTagTextField *textField = [[DSTagTextField alloc] init];
textField.deleteBlock = ^{
if (weakSelf.textField.hasText) return ;
[weakSelf tagButtonClick:[weakSelf.tagButtons lastObject]];
};
textField.delegate = self;
[textField becomeFirstResponder];
[textField addTarget:self action:@selector(textDidChange) forControlEvents:(UIControlEventEditingChanged)];
textField.width = self.contentView.width;
textField.height = 25;
textField.placeholder = @"多个标签使用逗号或换行分隔";
[self.contentView addSubview:textField];
self.textField = textField;
}
我们需要监听textField内部文字的改变,来判断如何显示底部添加按钮的文字显示(这里不可以用代理,因为代理不能判断点击键盘顶部的文字,这时我们发现textField的继承自UIControl,所以可以添加监听事件)
6 、监听textField内部文字的改变
/**
* 监听textField文字改变
*/
- (void)textDidChange
{
[self updateTextFieldFrame];
if ([self.textField hasText]) { // 有文字
self.addButton.hidden = NO;
self.addButton.y = CGRectGetMaxY(self.textField.frame) + DSTagButtonMargin;
[self.addButton setTitle:[NSString stringWithFormat:@"添加标签: %@", self.textField.text] forState:(UIControlStateNormal)];
// 获得最后一个字符
NSString *text = self.textField.text;
NSUInteger length = text.length;
NSString *lastText = [self.textField.text substringFromIndex:length - 1];
if ([lastText isEqualToString:@","] || [lastText isEqualToString:@","]) {
// 去除逗号
self.textField.text = [text substringToIndex:length - 1];
// 添加标签按钮
[self addButtonClick];
}else {
}
}else { // 没有文字
self.addButton.hidden = YES;
}
}
首先得创建“添加按钮”(用懒加载)
- (UIButton *)addButton
{
if (_addButton== nil) {
UIButton *addButton = [[UIButton alloc] init];
[addButton addTarget:self action:@selector(addButtonClick) forControlEvents:(UIControlEventTouchUpInside)];
addButton.width = self.contentView.width;
addButton.height = 35;
addButton.backgroundColor = DSTagBg;
// 设置内容水平向左对齐
addButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
addButton.titleLabel.font = DSTagFont;
// 左右间距
addButton.contentEdgeInsets = UIEdgeInsetsMake(0, DSTagButtonMargin, 0, DSTagButtonMargin);
[self.contentView addSubview:addButton];
self.addButton = addButton;
}
return _addButton;
}
我们要根据有没有文字来设置添加按钮的显示和隐藏。
7 、监听“添加按钮”的点击
/**
* 监听添加按钮的点击
*/
- (void)addButtonClick
{
if (self.tagButtons.count == 5) { // 最多显示五个标签按钮
[SVProgressHUD showErrorWithStatus:@"最多显示5个标签"];
return ;
}
// 添加 标签按钮
DSTagButton *tagButton = [[DSTagButton alloc] init];
[tagButton addTarget:self action:@selector(tagButtonClick:) forControlEvents:(UIControlEventTouchUpInside)];
[tagButton setTitle:[NSString stringWithFormat:@"%@", self.textField.text] forState:(UIControlStateNormal)];
[self.contentView addSubview:tagButton];
[self.tagButtons addObject:tagButton];
self.addButton.hidden = YES;
self.textField.text = nil;
// 更新标签按钮和textField的位置
[self updateTagButtonsFrame];
[self updateTextFieldFrame];
}
/**
* 更新标签按钮的位置
*/
- (void)updateTagButtonsFrame
{
for (int i = 0; i < self.tagButtons.count; i++) {
UIButton *tagButton = self.tagButtons[i];
if (i == 0) { // 第一个位置标签按钮
tagButton.x = 0;
tagButton.y = 0;
}else { // 其他位置的标签按钮
// 找出前一个标签按钮
UIButton *lastTagButton = self.tagButtons[i - 1];
// 左边按钮所占宽度
CGFloat leftWidth = CGRectGetMaxX(lastTagButton.frame);
// 右边剩余宽度
CGFloat rightWidth = self.contentView.width - leftWidth;
if (rightWidth >= tagButton.width) { // 按钮显示在当前行
tagButton.x = CGRectGetMaxX(lastTagButton.frame) + DSTagButtonMargin;
tagButton.y = lastTagButton.y;
}else { // 按钮显示在下一行
tagButton.x = 0;
tagButton.y = CGRectGetMaxY(lastTagButton.frame) + DSTagButtonMargin;
}
}
}
}
/**
* 更新textField的位置
*/
- (void)updateTextFieldFrame
{
UIButton *lastTagButton = [self.tagButtons lastObject];
CGFloat leftWidth = CGRectGetMaxX(lastTagButton.frame) + DSTagButtonMargin;
// 更新textField的frame
if (self.contentView.width - leftWidth - DSTagButtonMargin >= [self textFieldTextWidth]) {
self.textField.x = leftWidth;
self.textField.y = lastTagButton.y;
}else {
self.textField.x = 0;
self.textField.y = CGRectGetMaxY(lastTagButton.frame) + DSTagButtonMargin;
}
}
一旦点击了“添加按钮”后,我们应该及时更新“标签按钮”和“textField”的 位置!
8、监听“return”和“,”点击(相当于点击了“添加按钮”)
return键可以给textField设置代理来监听!
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField.hasText) {
[self addButtonClick];
}
return YES;
}
关于点击“,”,则是在textField文字改变时,拿出最后一个文字来判断!(上述代码已有写)
9、监听键盘删除按钮的点击
这个就不可以用textField的代理来解决了!而是应该重写textField的UIKeyInput协议里的deleteBackward方法,在这里告诉监听删除,由于得调用控制器方法,所以可以用Block来完成!在控制器中的block中写上删除操作,然后回调到textField的deleteBackward方法中来监听删除按钮的点击!(删除操作初始化textField代码中)
- (void)deleteBackward
{
!self.deleteBlock ? : self.deleteBlock();
[super deleteBackward];
}
10、发布界面和添加标签按钮界面之间的数据传递
这个时候,在我们添加了标签按钮后,需要在发布界面的工具条中更新按钮!所以可以在点击了完成item后,利用Block将当前的标签按钮文字数组传递过去,然后在push添加标签按钮控制器时,调用该block,根据传递的标签文字来创建label!
/**
* 右侧item点击
*/
- (void)done
{
// 传递数据给上一个控制器
// NSMutableArray *tags = [NSMutableArray array];
// for (DSTagButton *tagButton in self.tagButtons) {
// [tags addObject:tagButton.currentTitle];
// }
NSArray *tags = [self.tagButtons valueForKeyPath:@"currentTitle"];
// 将tags传递给这个block
!self.tagBlock ? : self.tagBlock(tags);
[self.navigationController popViewControllerAnimated:YES];
}
/**
* 创建标签label
*/
- (void)createTagLabels:(NSArray *)tags
{
// 移除之前的标签(一定要先移除lable,然后清空数据)
[self.tagLabels makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.tagLabels removeAllObjects];
// 1.创建标签label,并设置frame
for (int i = 0; i < tags.count; i++) {
UILabel *tagLabel = [[UILabel alloc] init];
tagLabel.textAlignment = NSTextAlignmentCenter;
tagLabel.text = tags[i];
tagLabel.textColor = [UIColor whiteColor];
tagLabel.backgroundColor = DSTagBg;
[tagLabel sizeToFit];
tagLabel.width += DSTagButtonMargin * 2;
[self.topView addSubview:tagLabel];
[self.tagLabels addObject:tagLabel];
if (i == 0) { // 第一个位置标签label
tagLabel.x = 0;
tagLabel.y = 0;
}else { // 其他位置的标签label
// 找出最后一个标签label
UILabel *lastTagLabel = self.tagLabels[i - 1];
// 左边label所占宽度
CGFloat leftWidth = CGRectGetMaxX(lastTagLabel.frame) + DSTagButtonMargin;
// 右边剩余宽度
CGFloat rightWidth = DSScreenW - 2 * DSTagButtonMargin - leftWidth;
if (rightWidth >= tagLabel.width) { // label显示在当前行
tagLabel.x = leftWidth;
tagLabel.y = lastTagLabel.y;
}else { // label显示在下一行
tagLabel.x = 0;
tagLabel.y = CGRectGetMaxY(lastTagLabel.frame) + DSTagButtonMargin;
}
}
}
// 2.更新+号按钮的frame
UILabel *lastTagLabel = [self.tagLabels lastObject];
// 标签lable所占的宽度
CGFloat leftWidth = CGRectGetMaxX(lastTagLabel.frame) + DSTagButtonMargin;
if (self.topView.width - leftWidth - DSTagButtonMargin >= self.addButton.width) { // +号按钮显示在当前行
self.addButton.x = leftWidth;
self.addButton.y = lastTagLabel.y;
}else { // +号按钮显示在下一行
self.addButton.x = 0;
self.addButton.y = CGRectGetMaxY(lastTagLabel.frame) + DSTagButtonMargin;
}
}
提示:这个调用block,根据标签文字创建label的代码已经在上面的+号按钮点击方法中了!
11、添加默认标签,自动算工具条高度
自动计算高度可以先算出+号按钮的最大y值,然后加上间距和工具条高度,就可以自动算出高度了!但是这个时候会有一个小问题,就是键盘在弹出后,工具条的y值就已经固定了,这个时候改变工具条的高度就会导致工具条往上移动,中间会空出一段距离!因此应该在算出工具条高度后,及时改变y值!
这一步写在- (void)createTagLabels:(NSArray *)tags方法中
// 3.更新高度
CGFloat oldH = self.height;
self.height = CGRectGetMaxY(self.addButton.frame) + 45;
self.y -= self.height - oldH;