项目实战No5 动画效果 占位文字
2015-09-09 22:56
429 查看
一 发布功能
功能有动画效果,动态最好用代码实现标语代码实现
建数组,填充按钮内容
创建/添加按钮
// 屏幕尺寸 #define XMGScreenH [UIScreen mainScreen].bounds.size.height #define XMGScreenW [UIScreen mainScreen].bounds.size.width @interface XMGPublishViewController () /** 标语 */ @property (nonatomic, weak) UIImageView *sloganView; @end
- (void)viewDidLoad { [super viewDidLoad]; // 标语 UIImageView *sloganView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"app_slogan"]]; sloganView.y = XMGScreenH * 0.15; sloganView.centerX = XMGScreenW * 0.5; [self.view addSubview:sloganView]; self.sloganView = sloganView; // 按钮 // 数据 NSArray *images = @[@"publish-video", @"publish-picture", @"publish-text", @"publish-audio", @"publish-review", @"publish-offline"]; NSArray *titles = @[@"发视频", @"发图片", @"发段子", @"发声音", @"审帖", @"离线下载"]; // 一些参数 NSUInteger count = images.count; int maxColsCount = 3; // 一行的列数 NSUInteger rowsCount = (count + maxColsCount - 1) / maxColsCount; // 按钮尺寸 CGFloat buttonW = XMGScreenW / maxColsCount; CGFloat buttonH = buttonW * 0.85; CGFloat buttonStartY = (XMGScreenH - rowsCount * buttonH) * 0.5; for (int i = 0; i < count; i++) { // 创建、添加 XMGPublishButton *button = [XMGPublishButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; // frame CGFloat buttonX = (i % maxColsCount) * buttonW; CGFloat buttonY = buttonStartY + (i / maxColsCount) * buttonH; button.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH); // 内容 [button setImage:[UIImage imageNamed:images[i]] forState:UIControlStateNormal]; [button setTitle:titles[i] forState:UIControlStateNormal]; } }
添加\布局子控件的方法
在viewDidLoad方法中创建、添加子控件(只创建/添加一次)
在viewDidLayoutSubviews方法中布局子控件
自定义按钮PublishButton
- (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.titleLabel.textAlignment = NSTextAlignmentCenter; self.titleLabel.font = [UIFont systemFontOfSize:15]; [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; self.imageView.y = 0; self.imageView.centerX = self.width * 0.5; self.titleLabel.width = self.width; self.titleLabel.y = CGRectGetMaxY(self.imageView.frame); self.titleLabel.x = 0; self.titleLabel.height = self.height - self.titleLabel.y; }
label高度有点高,调整按钮尺寸方法:
方法1. CGFloat buttonH = buttonW * 0.85;
方法2. 调整图片的y值往下移
二 动画弹簧效果
方法1:根据y值做动画效果,在做弹簧效果方法2:苹果系统自带框架:Core Animation
动画只能作用在CALayer上
无法监听到动画的中间值
方法3:第三方框架:pop
动画能作用在任何对象上
能监听到动画的中间值
pop动画效果代码
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame]; anim.fromValue = [NSValue valueWithCGRect:CGRectMake(buttonX, buttonY - XMGScreenH, buttonW, buttonH)]; anim.toValue = [NSValue valueWithCGRect:CGRectMake(buttonX, buttonY, buttonW, buttonH)]; anim.springSpeed = 10; anim.springBounciness = 10; // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + 0.2 * i; [button pop_addAnimation:anim forKey:nil];
弹簧效果代码
CGFloat sloganY = XMGScreenH * 0.2; // 动画 POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY]; anim.toValue = @(sloganY); anim.springSpeed = 10; anim.springBounciness = 10; // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + [self.times.lastObject doubleValue]; [sloganView.layer pop_addAnimation:anim forKey:nil];
如何实现指定下来按钮顺序
// 数据 NSArray *images = @[@"publish-video", @"publish-picture", @"publish-text", @"publish-audio", @"publish-review", @"publish-offline"]; NSArray *titles = @[@"发视频", @"发图片", @"发段子", @"发声音", @"审帖", @"离线下载"];
标语加入动画:
- (void)setupSloganView { CGFloat sloganY = XMGScreenH * 0.2; // 添加 UIImageView *sloganView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"app_slogan"]]; sloganView.y = sloganY - XMGScreenH; sloganView.centerX = XMGScreenW * 0.5; [self.view addSubview:sloganView]; self.sloganView = sloganView; // 动画 POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY]; anim.toValue = @(sloganY); anim.springSpeed = 10; anim.springBounciness = 10; // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + [self.times.lastObject doubleValue]; [sloganView.layer pop_addAnimation:anim forKey:nil]; }
时间间隔调整
- (NSArray *)times { if (!_times) { CGFloat interval = 0.1; // 时间间隔 _times = @[@(5 * interval), @(4 * interval), @(3 * interval), @(2 * interval), @(0 * interval), @(1 * interval), @(6 * interval)]; // 标语的动画时间 } return _times; }
按钮的尺寸为0,还是能看见文字缩成一个点,设置按钮的尺寸为负数,那么就看不见文字了 // button.width = -1;
三 退出控制器
先执行动画,再退出控制器动画执行过程中,开始和取消过程中,都不能点击按钮,禁止交互作用
// 禁止交互 self.view.userInteractionEnabled = NO;
- (IBAction)cancel { // 让按钮执行动画 for (int i = 0; i < self.buttons.count; i++) { XMGPublishButton *button = self.buttons[i]; POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY]; anim.toValue = @(button.layer.position.y + XMGScreenH); // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + [self.times[i] doubleValue]; [button.layer pop_addAnimation:anim forKey:nil]; } XMGWeakSelf; // 让标题执行动画 POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY]; anim.toValue = @(self.sloganView.layer.position.y + XMGScreenH); // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + [self.times.lastObject doubleValue]; [anim setCompletionBlock:^(POPAnimation *anim, BOOL finished) { [weakSelf dismissViewControllerAnimated:NO completion:nil]; }]; [self.sloganView.layer pop_addAnimation:anim forKey:nil]; }
点击蒙板也可以实现取消功能:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self cancel]; }
四 按钮点击功能完善
动画结束完毕后,实现点击按钮对应功能建一个“发段子”控制器
点击“发段子”,动画退出,弹出“发段子”控制器
- (void)viewDidLoad { [super viewDidLoad]; self.title = @"发表文字"; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消" style:UIBarButtonItemStyleDone target:self action:@selector(cancel)]; }
- (void)cancel { [self dismissViewControllerAnimated:YES completion:nil]; }
// 关闭当前控制器 [weakSelf dismissViewControllerAnimated:YES completion:nil]; // 按钮索引 NSUInteger index = [self.buttons indexOfObject:button]; switch (index) { case 2: { // 发段子 // 弹出发段子控制器 XMGPostWordViewController *postWord = [[XMGPostWordViewController alloc] init]; [self.view.window.rootViewController presentViewController:[[XMGNavigationController alloc] initWithRootViewController:postWord] animated:YES completion:nil]; }
五 代码重构
动画退出每次退出都要执行一次点击功能按钮退出,执行完退出动画,在弹出一个新的view
传block进去,执行动画退出后的操作
#pragma mark - 退出动画 - (void)exit:(void (^)())task { // 禁止交互 self.view.userInteractionEnabled = NO; // 让按钮执行动画 for (int i = 0; i < self.buttons.count; i++) { XMGPublishButton *button = self.buttons[i]; POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY]; anim.toValue = @(button.layer.position.y + XMGScreenH); // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + [self.times[i] doubleValue]; [button.layer pop_addAnimation:anim forKey:nil]; } XMGWeakSelf; // 让标题执行动画 POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY]; anim.toValue = @(self.sloganView.layer.position.y + XMGScreenH); // CACurrentMediaTime()获得的是当前时间 anim.beginTime = CACurrentMediaTime() + [self.times.lastObject doubleValue]; [anim setCompletionBlock:^(POPAnimation *anim, BOOL finished) { [weakSelf dismissViewControllerAnimated:NO completion:nil]; // block调空的时候会报错,增加判断: // 可能会做其他事情 if (task) task(); // !task ? : task(); }]; [self.sloganView.layer pop_addAnimation:anim forKey:nil]; }
#pragma mark - 点击 - (void)buttonClick:(XMGPublishButton *)button { [self exit:^{ // 按钮索引 NSUInteger index = [self.buttons indexOfObject:button]; switch (index) { case 2: { // 发段子 // 弹出发段子控制器 XMGPostWordViewController *postWord = [[XMGPostWordViewController alloc] init]; [self.view.window.rootViewController presentViewController:[[XMGNavigationController alloc] initWithRootViewController:postWord] animated:YES completion:nil]; break; } case 1: XMGLog(@"发图片"); break; default: XMGLog(@"其它"); break; } }]; }
五 占位文字
UITextField有占位文字
最多只能输入一行文字
UITextView
没有占位文字
能输入任意行文字
如何实现既有占位文字,也能输入任意行文字?
解决方案:继承自UITextView,增加一个功能有占位文字
有导航栏的话,苹果系统会自动增加64间距,如果不需要的话:
方法1:不要自动调整scrollView的contentInet属性
self.automaticallyAdjustsScrollViewInsets = NO;
方法2: 开始就设置contentInset
textView.contentInset = UIEdgeInsetsMake(-64, 0, 0, 0);
六 占位文字实现1
占位文字画到view上/** * 每次调用drawRect:方法,都会将以前画的东西清除掉 */ - (void)drawRect:(CGRect)rect { // 如果有文字,就直接返回,不需要画占位文字 // if (self.text.length || self.attributedText.length) return; if (self.hasText) return; // 属性 NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; attrs[NSFontAttributeName] = self.font; attrs[NSForegroundColorAttributeName] = self.placeholderColor; // 画文字 // 光标和占位文字对不上调整 rect.origin.x = 5; rect.origin.y = 8; rect.size.width -= 2 * rect.origin.x; [self.placeholder drawInRect:rect withAttributes:attrs]; }
使用通知监听文字多少改变
- (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // 设置默认字体 self.font = [UIFont systemFontOfSize:15]; // 设置默认颜色 self.placeholderColor = [UIColor grayColor]; // 使用通知监听文字改变 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self]; } return self; } - (void)textDidChange:(NSNotification *)note { // 会重新调用drawRect:方法 [self setNeedsDisplay]; } // 移除通知 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
监听属性修改,重写set方法
#pragma mark - setter // 占位文字变化 - (void)setPlaceholder:(NSString *)placeholder { _placeholder = [placeholder copy]; [self setNeedsDisplay]; } // 占位文字颜色变化 - (void)setPlaceholderColor:(UIColor *)placeholderColor { _placeholderColor = placeholderColor; [self setNeedsDisplay]; } // 占位文字字体变化 - (void)setFont:(UIFont *)font { [super setFont:font]; [self setNeedsDisplay]; } // 占位文字变化(用户操作) - (void)setText:(NSString *)text { [super setText:text]; [self setNeedsDisplay]; }
textView尺寸发生变化时,
- (void)layoutSubviews { [super layoutSubviews]; [self setNeedsDisplay]; }
往下拖拽可以退出键盘:
遵守UITextView代理
// 不管内容有多少,竖直方向上永远可以拖拽 textView.alwaysBounceVertical = YES; #pragma mark - <UITextViewDelegate> - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self.view endEditing:YES]; }
七 占位文字实现2
TextView内部添加UILabel,控制Label是否隐藏- (void)textDidChange:(NSNotification *)note { self.placeholderLabel.hidden = self.hasText; }
- (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // 创建label UILabel *placeholderLabel = [[UILabel alloc] init]; placeholderLabel.numberOfLines = 0; [self addSubview:placeholderLabel]; self.placeholderLabel = placeholderLabel; // 设置默认字体 self.font = [UIFont systemFontOfSize:15]; // 设置默认颜色 self.placeholderColor = [UIColor grayColor]; // 使用通知监听文字改变 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self]; } return self; }
监听改变,重写 set方法,直接操作label
#pragma mark - setter - (void)setPlaceholder:(NSString *)placeholder { _placeholder = [placeholder copy]; self.placeholderLabel.text = placeholder; [self.placeholderLabel sizeToFit]; } - (void)setPlaceholderColor:(UIColor *)placeholderColor { _placeholderColor = placeholderColor; self.placeholderLabel.textColor = placeholderColor; } - (void)setFont:(UIFont *)font { [super setFont:font]; self.placeholderLabel.font = font; [self.placeholderLabel sizeToFit]; }
UILabel自动垂直居中,不换行
- (void)layoutSubviews { [super layoutSubviews]; self.placeholderLabel.x = 5; self.placeholderLabel.y = 8; self.placeholderLabel.width = self.width - 2 * self.placeholderLabel.x; [self.placeholderLabel sizeToFit]; }
八 发表功能实现
发表功能,有文字时可以点击- (void)textViewDidChange:(UITextView *)textView { self.navigationItem.rightBarButtonItem.enabled = textView.hasText; }
设置Navigation标题大小、ButtonItem颜色
+ (void)initialize { /** 设置UINavigationBar */ UINavigationBar *bar = [UINavigationBar appearance]; // 设置背景 [bar setBackgroundImage:[UIImage imageNamed:@"navigationbarBackgroundWhite"] forBarMetrics:UIBarMetricsDefault]; // 设置标题文字属性 NSMutableDictionary *barAttrs = [NSMutableDictionary dictionary]; barAttrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:20]; [bar setTitleTextAttributes:barAttrs]; /** 设置UIBarButtonItem */ UIBarButtonItem *item = [UIBarButtonItem appearance]; // UIControlStateNormal NSMutableDictionary *normalAttrs = [NSMutableDictionary dictionary]; normalAttrs[NSForegroundColorAttributeName] = [UIColor blackColor]; normalAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:17]; [item setTitleTextAttributes:normalAttrs forState:UIControlStateNormal]; // UIControlStateDisabled NSMutableDictionary *disabledAttrs = [NSMutableDictionary dictionary]; disabledAttrs[NSForegroundColorAttributeName] = [UIColor grayColor]; [item setTitleTextAttributes:disabledAttrs forState:UIControlStateDisabled]; }
强制更新刷新,能马上刷新现在的状态
// 强制更新(能马上刷新现在的状态) [self.navigationController.navigationBar layoutIfNeeded];
类似方法对比
[tempView layoutIfNeeded];
// 重新刷新自己和子控件的所有内容(状态、尺寸)
[tempView setNeedsLayout];
// 重新调用tempView的layoutSubviews(重新排布子控件的frame)
[tempView setNeedsDisplay];
// 重新调用tempView的drawRect:方法(重新绘制tempView里面的内容,一般不包括子控件)
九 bounds
frame指的是该view在父view坐标系统中的位置和大小。(参照点是父控件的坐标系统)bounds指的是该view在本身坐标系统中的位置和尺寸。(参照点是本身内容左上角为坐标原点)
一个控件bounds的x\y是正数,这个控件的内容会往外面跑
一个控件bounds的x\y是负数,这个控件的内容会往里面跑
当控制器是导航控制器,并有导航栏时,bounds自动设置64间距
storyboard调整如下:
同上,设置self.automaticallyAdjustsScrollViewInsets = NO;
scrollView.contentSize; // 滚动范围(内容的尺寸)
scrollView.contentInset; // 内边距
scrollView.frame; // 以父控件内容的左上角为坐标原点,scrollView矩形框的位置和尺寸
scrollView.bounds; // 以自己内容的左上角为坐标原点,scrollView矩形框的位置和尺寸
scrollView.contentOffset; // 偏移量(scrollView.bounds.origin)
相关文章推荐
- hdu 5327 Olympiad
- ARM linux内核启动时几个关键地址
- javascript ES 6 class 详解
- Codeforces Round #318 (Div. 2) C - Bear and Poker
- C#实现在线更新
- 关于mount nfs时-o nolock的问题
- 2015-09-09
- R::shiny 点击事件-Demo
- How To Make Cscope Database by Shell
- 09.09 第一次上机作业
- Android之 -WebView实现离线缓存阅读
- UI线程阻塞及优化
- 中介者模式(Mediator Pattern)
- iCheat IC 使用教程
- 算法的效率的度量方法
- oracle 学习blogs
- UIAlertView+Blocks.h
- 格雷码计数器
- 分金币 (UVA 11300)
- VS2010 debug output not showing up in DbgView