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

代码地址如下:
http://www.demodashi.com/demo/12753.html

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

项目结构图

代码地址如下:
http://www.demodashi.com/demo/12753.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

原文地址:https://www.cnblogs.com/demodashi/p/9436586.html

时间: 2024-11-10 14:15:59

iOS仿支付宝首页的刷新布局效果的相关文章

仿支付宝/微信的password输入框效果GridPasswordView解析

仿支付宝/微信的password输入框效果GridPasswordView解析,把一些设置和一些关键的地方列了出来,方便大家使用,可能能够省一部分的时间,也算是自己的积累吧. 1.password框能够输入的类型PasswordType public enum PasswordType { NUMBER, TEXT, TEXTVISIBLE, TEXTWEB; } 这里能够自行加入类型,然后在GridPasswordView中改动 @Override public void setPasswor

仿支付宝首页头部伸缩效果

原文链接:https://mp.weixin.qq.com/s/GegMt7GDBCFVoUgFQWG3Sw 每次打开支付宝首页滑动,头部的伸缩动画甚是吸引人.于是自己决定动手来实现一个. 无图言虚空,效果图: 首先看到这种效果,第一反应就是coordinatorLayout布局,android studio新建项目时,可以直接新建个Scrolling Activity来查看demo效果. 官方自带的布局示例: gradle关联 implementation 'com.android.suppo

【开源项目解析】仿支付宝付款成功及&quot;天女散花&quot;效果实现——看PathMeasure大展身手

话说,在前面两篇文章中,我们学习了BitmapShader.Path的基本使用,那么这一篇文章,咱们接着来学习一下PathMeasure的用法.什么,你没听说过PathMeasure?那你就要OUT咯~ 项目效果图 PathMeasure介绍 仿支付宝实现原理解析 天女散花实现效果解析 更多参考资料 项目效果图 废话不多说,在开始讲解之前,先看下最终实现的效果. 效果一: 仿支付宝支付成功效果 效果二: 这两个项目都是使用Path和PathMeature配合完成的,由其他项目改造而来 项目一是七

React Native之ViewPagerAndroid仿淘宝首页顶部分类布局效果实现

转载请注明出处:http://blog.csdn.net/woshizisezise/article/details/51030410 大家好,趁着现在别人都去吃饭的时间,来给大家讲一讲React Native中Android部分的控件ViewPagerAndroid的讲解,这里特别提醒一下,我写的博客都是基于大家有一些React Native基础的前提下,因为关于React Native这一系列的博文我是最近开始更新,由于自身工作繁重(每天早上10点干到晚上10点,是不是很惨?),所以暂时还没

iOS仿UC浏览器顶部频道滚动效果

本人很喜欢用UC浏览器上网,当然不是给UC打广告,UC里很多动画效果很酷炫,指的学习,这次分享的是频道滚动的效果.动画效果如下: 实现的这个效果的关键是绘制,重写顶部Label的drawRect方法 github链接 https://github.com/losedMemory/ZSScrollViewForTheNews

CSS3实战开发:仿天猫首页图片展示动画特效实战开发

各位网友大家好,我是陌上花会开,人称陌陌.今天我带领大家开发一个仿天猫首页图片展示动画特效的案例.一如往常,我既不提供源码给大家,也不提供Demo给大家,只是希望大家能跟着我的讲解,然后将代码一步步复制到本地,本人可以保证,页面上的代码复制到本地,绝对百分百运行,且得到与陌陌一样的运行效果.我这么做只为激起大伙的动手能力,望大家能明白我的用心. 好了,不废话了,直接本篇的实战开发吧. 我们看一下我们今天要做的实战案例效果图: 1. 鼠标划过前: 2. 鼠标划过右侧的图片时: 可能大伙看这个静态截

Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现

Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现 2015-03-10 22:38 28419人阅读 评论(17) 收藏 举报  分类: Android UI(819)  Android开发(1568)  关注finddreams:http://blog.csdn.net/finddreams/article/details/43486527 今天我们来模仿一下支付宝钱包首页中带有分割线的GridView,俗称九宫格.先上图,是你想要的效果么?如果

[ios仿系列]仿支付宝手势解码

呀~.这么快就转到ios阵营了???.android还有那么多坑呢???为此我也仅仅能啃着馒头留下屈辱的眼泪了. . 本次因为开发公司产品的android版,继而ios版也负责一部分.当中一部分就是手势解锁的问题(为了要与android版一致,也算是非了一部分心血) 然而.但我打开iPhone,发现支付宝client已经没有手势解锁了,取而代之的时指纹解锁,关于指纹解锁,临时还来不及研究,仅仅能以后有机会再说了.. . 当然,支付宝android眼下还是手势解锁,所以也是为了与之相应(ios版如

仿支付宝/微信的密码输入框效果GridPasswordView解析

仿支付宝/微信的密码输入框效果GridPasswordView解析,把一些设置和一些关键的地方列了出来,方便大家使用,可能可以省一部分的时间,也算是自己的积累吧. 1.密码框可以输入的类型PasswordType public enum PasswordType { NUMBER, TEXT, TEXTVISIBLE, TEXTWEB; } 这里可以自行添加类型,然后在GridPasswordView中修改 @Override public void setPasswordType(Passwo