iOS仿支付宝首页的刷新布局效果
XYAlipayRefreshDemo
运行效果

动画效果分析
1.UI需要变动,向上滑动的时候,顶部部分收缩。
2.右侧的滚动条的位置是从中间开始的,说明一个现象,这个tableview是从这里开始的。
3.tableview上面的view,是给tableview一起滑动,做到了无缝衔接。
4.滑动 tableview 上面那块 view ,直接响应滑动。
实现思路: 怎么解决以上四个效果问题,是本次的重点。经过多次的尝试分析得出,采用UIScrollView加UITableView的方式实现。从右侧滚动条开始的位置,上部分是个view,下部分是个UITableView。topView和UITableView作为UIScrollView的subView。
关键点分析
1.实现scrollView的代理方法scrollViewDidScroll,设置顶部topview的导航栏位置的视图的高度为KTopHeaderViewHeight。当scrollView的y轴偏移量大于0且小于KTopHeaderViewHeight时,计算出透明度的变化。
CGFloat alpha = (offsetY*2/KTopHeaderViewHeight>0) ? offsetY*2/KTopHeaderViewHeight:0;
if (alpha > 0.5) {
self.navNewView.alpha = alpha*2-1;
self.navView.alpha = 0;
} else {
self.navNewView.alpha = 0;
self.navView.alpha = 1-alpha*2;
}
self.topHeaderView.alpha = 1-alpha;
2.滚动条是从中部开始,也是从tableView的位置开始,因此设置scrollView的Indicator的显示位置。其中KTopViewHeight的高度是滚动条开始以上位置的高度,整个topView的高度。
_scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(KTopViewHeight, 0, 0, 0);
3.scrollView和tableView同时具有滚动效果,当不做处理是,会出现滑动冲突。因此设置tableView的scrollEnabled为NO,解决滑动冲突。
_tableView.scrollEnabled = NO;
4.tableView作为scrollView的子视图,tableView的内容会随着刷新加载变化,为了正常的显示全部能容,需要监听tableView的contentSize,实时改变scrollView的contentSize
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"contentSize"]) {
CGRect tFrame = self.tableView.frame;
tFrame.size.height = self.tableView.contentSize.height;
self.tableView.frame = tFrame;
self.scrollView.contentSize = CGSizeMake(0, self.tableView.contentSize.height+KTopViewHeight);
}
}
5.在顶部视图收缩时,页面滑动时,topView和tableView的相对ScrollView的位置在发生变化。因此在UIScrollView的代理里面改变topView和tableView的frame值。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = self.scrollView.contentOffset.y;
if (offsetY <= 0.0) {
CGRect frame = self.topView.frame;
frame.origin.y = offsetY;
self.topView.frame = frame;
CGRect tFrame = self.tableView.frame;
tFrame.origin.y = offsetY + KTopViewHeight;
self.tableView.frame = tFrame;
if (![self.tableView isRefreshing]) {
self.tableView.contentOffset = CGPointMake(0, offsetY);
}
} else if (offsetY < KTopHeaderViewHeight && offsetY >0) {
CGFloat alpha = (offsetY*2/KTopHeaderViewHeight>0) ? offsetY*2/KTopHeaderViewHeight:0;
if (alpha > 0.5) {
self.navNewView.alpha = alpha*2-1;
self.navView.alpha = 0;
} else {
self.navNewView.alpha = 0;
self.navView.alpha = 1-alpha*2;
}
self.topHeaderView.alpha = 1-alpha;
}
}
注:下拉刷新控件是本人自己写的。可以替换成任意需要的控件,例如:MJRefresh,在相应的位置修改即可。
6、完整代码如下:
#import "ViewController.h"
#import "UIScrollView+XYRefreshView.h"
#define KScreenWidth [UIScreen mainScreen].bounds.size.width
#define KScreenHeight [UIScreen mainScreen].bounds.size.height
#define KTopViewHeight 300
#define KTopHeaderViewHeight 80
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource,XYPullRefreshViewDelegate,XYPushRefreshViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UIView *topView;
@property (nonatomic, strong) UIView *navBGView;
@property (nonatomic, strong) UIView *navView;
@property (nonatomic, strong) UIView *navNewView;
@property (nonatomic, strong) UIView *topHeaderView;
@property (nonatomic, assign) NSInteger dataCount;
@end
@implementation ViewController
- (void)dealloc {
[self removeObserver:self forKeyPath:@"contentSize"];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.navBGView];
[self.view addSubview:self.navView];
[self.view addSubview:self.navNewView];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.topView];
[self.scrollView addSubview:self.tableView];
//添加刷新控件
[self.tableView showPullRefreshViewWithDelegate:self];
[self.scrollView showPushRefreshViewWithDelegate:self];
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
self.dataCount = 20;
}
//根据tableView的contentSize改变scrollView的contentSize大小
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"contentSize"]) {
CGRect tFrame = self.tableView.frame;
tFrame.size.height = self.tableView.contentSize.height;
self.tableView.frame = tFrame;
self.scrollView.contentSize = CGSizeMake(0, self.tableView.contentSize.height+KTopViewHeight);
}
}
#pragma mark - XYPullRefreshViewDelegate
- (void)pullRefreshViewStartLoad:(XYPullRefreshView *)pullView {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[pullView endRefreshing];
self.dataCount += 5;
[self.tableView reloadData];
});
}
- (void)pushRefreshViewStartLoad:(XYPushRefreshView *)pushView {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[pushView endRefreshing];
self.dataCount += 5;
[self.tableView reloadData];
});
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = self.scrollView.contentOffset.y;
if (offsetY <= 0.0) {
CGRect frame = self.topView.frame;
frame.origin.y = offsetY;
self.topView.frame = frame;
CGRect tFrame = self.tableView.frame;
tFrame.origin.y = offsetY + KTopViewHeight;
self.tableView.frame = tFrame;
if (![self.tableView isRefreshing]) {
self.tableView.contentOffset = CGPointMake(0, offsetY);
}
} else if (offsetY < KTopHeaderViewHeight && offsetY >0) {
CGFloat alpha = (offsetY*2/KTopHeaderViewHeight>0) ? offsetY*2/KTopHeaderViewHeight:0;
if (alpha > 0.5) {
self.navNewView.alpha = alpha*2-1;
self.navView.alpha = 0;
} else {
self.navNewView.alpha = 0;
self.navView.alpha = 1-alpha*2;
}
self.topHeaderView.alpha = 1-alpha;
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
CGFloat offsetY = self.scrollView.contentOffset.y;
if (offsetY < - 60) {
[self.tableView startPullRefreshing];
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CELL"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CELL"];
}
cell.textLabel.text = [NSString stringWithFormat:@"%d",(int)indexPath.row];
return cell;
}
#pragma mark - getter && setter
- (UIScrollView *)scrollView {
if (_scrollView == nil) {
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 64, KScreenWidth, KScreenHeight-64)];
_scrollView.delegate = self;
//Indicator的显示位置
_scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(KTopViewHeight, 0, 0, 0);
_scrollView.contentSize = CGSizeMake(0, KScreenHeight*2);
}
return _scrollView;
}
- (UITableView *)tableView {
if (_tableView == nil) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, KTopViewHeight, KScreenWidth, KScreenHeight*2-KTopViewHeight)];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.scrollEnabled = NO;
}
return _tableView;
}
- (UIView *)topView {
if (_topView == nil) {
_topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KTopViewHeight)];
_topView.backgroundColor = [UIColor colorWithRed:66/255.0 green:128/255.0 blue:240/255.0 alpha:1];
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KTopHeaderViewHeight)];
headerView.backgroundColor = [UIColor colorWithRed:66/255.0 green:128/255.0 blue:240/255.0 alpha:1];
for (int i = 0; i<4; i++) {
UIButton *button = [[UIButton alloc] init];
[button setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%d",i+1]] forState:UIControlStateNormal];
[headerView addSubview:button];
button.frame = CGRectMake((KScreenWidth/4)*i, 0, KScreenWidth/4, KTopHeaderViewHeight);
}
self.topHeaderView = headerView;
[_topView addSubview:headerView];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, KTopHeaderViewHeight, KScreenWidth, KTopViewHeight-KTopHeaderViewHeight)];
view.backgroundColor = [UIColor brownColor];
[_topView addSubview:view];
}
return _topView;
}
- (UIView *)navBGView {
if (_navBGView == nil) {
_navBGView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, 64)];
_navBGView.backgroundColor = [UIColor colorWithRed:66/255.0 green:128/255.0 blue:240/255.0 alpha:1];
}
return _navBGView;
}
- (UIView *)navView {
if (_navView == nil) {
_navView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, 64)];
_navView.backgroundColor = [UIColor clearColor];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(10, 20, 200, 44)];
searchBar.backgroundImage = [[UIImage alloc] init];
UITextField *searchField = [searchBar valueForKey:@"searchField"];
if (searchField) {
[searchField setBackgroundColor:[UIColor colorWithRed:230/255 green:230/250 blue:230/250 alpha:0.1]];
}
[_navView addSubview:searchBar];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(220, 20, 44, 44)];
[button setImage:[UIImage imageNamed:@"contacts"] forState:UIControlStateNormal];
[_navView addSubview:button];
}
return _navView;
}
- (UIView *)navNewView {
if (_navNewView == nil) {
_navNewView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, 64)];
_navNewView.backgroundColor = [UIColor clearColor];
UIButton *cameraBtn = [[UIButton alloc] initWithFrame:CGRectMake(10, 20, 44, 44)];
[cameraBtn setImage:[UIImage imageNamed:@"nav_camera"] forState:UIControlStateNormal];
[_navNewView addSubview:cameraBtn];
UIButton *payBtn = [[UIButton alloc] initWithFrame:CGRectMake(64, 20, 44, 44)];
[payBtn setImage:[UIImage imageNamed:@"nav_pay"] forState:UIControlStateNormal];
[_navNewView addSubview:payBtn];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(118, 20, 44, 44)];
[button setImage:[UIImage imageNamed:@"nav_scan"] forState:UIControlStateNormal];
[_navNewView addSubview:button];
_navNewView.alpha = 0;
}
return _navNewView;
}
@end
项目结构图

iOS仿支付宝首页的刷新布局效果
注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权
iOS仿支付宝首页的刷新布局效果的更多相关文章
- iOS仿支付宝首页效果
代码地址如下:http://www.demodashi.com/demo/12776.html 首先看一下效果 状态栏红色是因为使用手机录屏的原因. 1.问题分析 1.导航栏A有两组控件,随着tabl ...
- Android控件Gridview实现仿支付宝首页,Fragment底部按钮切换和登录圆形头像
此案例主要讲的是Android控件Gridview(九宫格)完美实现仿支付宝首页,包含添加和删除功能:Fragment底部按钮切换的效果,包含四个模块,登录页面圆形头像等,一个小项目的初始布局. 效果 ...
- android支付宝首页、蚂蚁森林效果、视频背景、校园电台、载入收缩动画等源码
Android精选源码 android实现蚂蚁森林效果源码 android仿支付宝首页应用管理(拖拽排序,添加删除) android校园网络电台客户端源码 android实现按钮伸缩效果源码 一款仿i ...
- iOS 仿支付宝密码支付
代码地址如下:http://www.demodashi.com/demo/11484.html 一.准备工作 xcode 主要实现输入密码的时候不可见 二.程序实现 实现思路怎样 在支付宝输入密码的时 ...
- 仿支付宝/微信的password输入框效果GridPasswordView解析
仿支付宝/微信的password输入框效果GridPasswordView解析,把一些设置和一些关键的地方列了出来,方便大家使用,可能能够省一部分的时间,也算是自己的积累吧. 1.password框能 ...
- iOS仿支付宝芝麻信用仪表盘效果
概述 自定义View之高仿支付宝芝麻信用分数仪表盘动画效果 详细 代码下载:http://www.demodashi.com/demo/10654.html 仿支付宝芝麻信用仪表盘效果 一.主要思路 ...
- iOS仿UC浏览器顶部频道滚动效果
很喜欢用UC浏览器上网,当然不是给UC打广告,里面有很多酷炫的效果,值的学习,这次分享的是频道滚动的效果.动画效果如下: 实现的这个效果的关键是绘制,重写顶部Label的drawRect方法 gith ...
- iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码
iOS精选源码 iOS WKWebView的使用源码 模仿apple music 小播放器的交互实现 高仿微信的悬浮小窗口 iOS仿支付宝首页效果 [swift]仿微信悬浮窗 类似于今日头条,网易新闻 ...
- ViewPager+GridView实现首页导航栏布局分页效果
如图是效果图用ViewPager+GridView实现首页导航栏布局分页效果来实现的效果 Demo下载地址:http://download.csdn.net/detail/qq_29774291/96 ...
随机推荐
- tcpreplay 缓存算法研究
一. 缓存算法 1.1 算法目的 流量拆分算法的运算会明显影响包的发送速率,为了提高发送速率, tcpreplay 使用了缓存机制,该部分代码也封装在tcpprep工具里,运行 tcpprep ( ...
- 【leetcode】500. Keyboard Row
问题描述: Given a List of words, return the words that can be typed using letters of alphabet on only on ...
- 无法解析的外部符号 "public: static void __cdecl std::_String_base::_Xran(void)" (?_Xran@_String_base@std@@SAXXZ)"
采用下面的方法.重新编译了一下依赖的库,OK了. 问题描述: 今天用VS2010编译一个在VS2008下Coding的工程的时候,VS给出了一堆链接错误信息,如下图所示: 在ErrorList里面 ...
- python--websocket数据解析
# websocket实现原理 ''' 1.服务端开启socket,监听ip和端口 2.客户端发送连接请求(带上ip和端口) 3.服务端允许连接 4.客户端生成一个随机字符串,和magic strin ...
- Flex接收51单片机发送过来的16进制数据转换为String
private static function toHex(bytes:ByteArray):String{ var pos:int =bytes.position; bytes.position=0 ...
- windows 中使用hbase 异常:java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
平时一般是在windows环境下进行开发,在windows 环境下操作hbase可能会出现异常(java.io.IOException: Could not locate executable nul ...
- 负载均衡技术之-lvs
LVS简介 Internet的快速增长使多媒体网络服务器面对的访问数量快速增加,服务器需要具备提供大量并发访问服务的能力,因此对于大负载的服务器来讲, CPU.I/O处理能力很快会成为瓶颈.由于单台服 ...
- 处理eq问题
using System;using System.Diagnostics;using System.Drawing;using System.IO;using System.Windows.Form ...
- 如何配置openjdk的 java home
https://blog.csdn.net/redmoon729/article/details/51671354
- Python与数据库[2] -> 关系对象映射/ORM[2] -> 建立声明层表对象的两种方式
建立声明层表对象的两种方式 在对表对象进行建立的时候,通常有两种方式可以完成,以下是两种方式的建立过程对比 首先导入需要的模块,获取一个声明层 from sqlalchemy.sql.schema i ...