Commit 3cd351da authored by leijunbo's avatar leijunbo

新加红包兑换

parent 279e3c89
//
// CTDiskCacheCenter.m
// Open
//
// Created by 雷俊博 on 2019/8/26.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "CTDiskCacheCenter.h"
NSString * const kCTDiskCacheCenterCachedObjectKeyPrefix = @"kCTDiskCacheCenterCachedObjectKeyPrefix";
@implementation CTDiskCacheCenter
+ (id)fetchCachedRecordWithKey:(NSString *)key
{
NSString *actualKey = [NSString stringWithFormat:@"%@%@",kCTDiskCacheCenterCachedObjectKeyPrefix, key];
id response = nil;
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:actualKey];
if (data) {
NSDictionary *fetchedContent = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSNumber *lastUpdateTimeNumber = fetchedContent[@"lastUpdateTime"];
NSDate *lastUpdateTime = [NSDate dateWithTimeIntervalSince1970:lastUpdateTimeNumber.doubleValue];
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:lastUpdateTime];
if (timeInterval < [fetchedContent[@"cacheTime"] doubleValue]) {
response = fetchedContent[@"content"];
} else {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:actualKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
return response;
}
+ (void)saveCacheWithResponse:(id)response key:(NSString *)key cacheTime:(NSTimeInterval)cacheTime
{
if (response) {
NSData *data = [NSJSONSerialization dataWithJSONObject:@{
@"content":response,
@"lastUpdateTime":@([NSDate date].timeIntervalSince1970),
@"cacheTime":@(cacheTime)
}
options:0
error:NULL];
if (data) {
NSString *actualKey = [NSString stringWithFormat:@"%@%@",kCTDiskCacheCenterCachedObjectKeyPrefix, key];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:actualKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
}
+ (void)cleanAll
{
NSDictionary *defaultsDict = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSArray *keys = [[defaultsDict allKeys] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF beginswith[c] %@", kCTDiskCacheCenterCachedObjectKeyPrefix]];
for(NSString *key in keys) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
[[NSUserDefaults standardUserDefaults] synchronize];
}
@end
//
// Home3MiddleBannerCell.h
// Open
//
// Created by 雷俊博 on 2019/8/27.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol MiddleBannerViewCellDelegate <NSObject>
- (void)selectedItemCell:(NSInteger)index;
@end
@interface Home3MiddleBannerCell : UITableViewCell
@property (nonatomic, weak)id<MiddleBannerViewCellDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
//
// Home3MiddleBannerCell.m
// Open
//
// Created by 雷俊博 on 2019/8/27.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "Home3MiddleBannerCell.h"
#import "PDBannerView.h"
@interface Home3MiddleBannerCell ()<PDBannerViewDelegate>
@property (nonatomic, strong)PDBannerView *bannerView;
@property (nonatomic, strong)NSMutableArray *dataArray;
@end
@implementation Home3MiddleBannerCell
#pragma mark - life cycle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubview:self.bannerView];
}
return self;
}
#pragma mark - PDBannerViewDelegate
- (void)selectImage:(PDBannerView *)bannerView currentImage:(NSInteger)currentImage
{
NSLog(@"中间广告点击图片索引%ld",(long)currentImage);
if (self.delegate && [self.delegate respondsToSelector:@selector(selectedItemCell:)]) {
[self.delegate selectedItemCell:currentImage];
}
}
#pragma mark - public methods
#pragma mark - getters and setters
- (PDBannerView *)bannerView
{
if (!_bannerView) {
UIImage *image = [UIImage imageNamed:@"190123-微保乘驾险-V2.png"];
float width = image.size.width;
float height = image.size.height;
float widthProportion = ScreenWidth / width;
_bannerView = [[PDBannerView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, height * widthProportion) andImageArray:self.dataArray];
_bannerView.delegate = self;
}
return _bannerView;
}
- (NSMutableArray *)dataArray
{
if (!_dataArray) {
NSArray *imageNamesArray = @[@"middleBanner",@"190123-微保乘驾险-V2",@"airInsurance"];
_dataArray = [[NSMutableArray alloc]initWithArray:imageNamesArray];
}
return _dataArray;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end
//
// OfficialAccountsAlertView.h
// Open
//
// Created by 雷俊博 on 2019/8/20.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^clickActionBlock) (BOOL);
@interface OfficialAccountsAlertView : UIView
@property (nonatomic, copy) clickActionBlock block;
@end
NS_ASSUME_NONNULL_END
//
// OfficialAccountsAlertView.m
// Open
//
// Created by 雷俊博 on 2019/8/20.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "OfficialAccountsAlertView.h"
#import "Constants.h"
#import "UIColor+Hex.h"
@interface OfficialAccountsAlertView ()
@property (nonatomic, strong)UILabel *lb_title;
@property (nonatomic, strong)UIImageView *img_message;
@property (nonatomic, strong)UIButton *btn_click;
@property (nonatomic, assign)BOOL show;
@end
@implementation OfficialAccountsAlertView
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.lb_title];
[self addSubview:self.img_message];
[self addSubview:self.btn_click];
self.show = YES;
[self setupUI];
}
return self;
}
- (void)setupUI
{
[self.lb_title mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(16 * WIDTHRADIU);
make.centerX.equalTo(self);
make.height.offset(15 * WIDTHRADIU);
}];
UIImage *image = [UIImage imageNamed:@"TencentTask"];
CGFloat scale = image.size.height/image.size.width;
[self.img_message mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_title.mas_bottom).offset(16 * WIDTHRADIU);
make.centerX.equalTo(self);
make.width.offset(250 * WIDTHRADIU);
make.height.offset(250 * WIDTHRADIU * scale);
}];
[self.btn_click mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.img_message.mas_bottom).offset(20 * WIDTHRADIU);
make.width.offset(230 * WIDTHRADIU);
make.centerX.equalTo(self);
make.height.offset(40 * WIDTHRADIU);
}];
}
#pragma mark - event response
- (void)btnClick:(UIButton *)sender
{
if (self.block) {
self.block(self.show);
}
}
#pragma mark - getters and setters
- (UILabel *)lb_title
{
if (!_lb_title) {
_lb_title = [[UILabel alloc]init];
_lb_title.text = @"公众号账号:看看社保";
_lb_title.font = [UIFont boldSystemFontOfSize:15 * WIDTHRADIU];
_lb_title.textColor = [UIColor colorWithHex:0x4d4d4d];
_lb_title.textAlignment = NSTextAlignmentCenter;
}
return _lb_title;
}
- (UIImageView *)img_message
{
if (!_img_message) {
_img_message = [[UIImageView alloc]init];
_img_message.image = [UIImage imageNamed:@"TencentTask"];
}
return _img_message;
}
- (UIButton *)btn_click
{
if (!_btn_click) {
_btn_click = [UIButton buttonWithType:UIButtonTypeCustom];
_btn_click.backgroundColor = [UIColor colorWithHex:0xfa9504];
[_btn_click setTitle:@"复制账号并关注" forState:UIControlStateNormal];
[_btn_click setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_btn_click.titleLabel.font = [UIFont systemFontOfSize:15 * WIDTHRADIU];
[_btn_click addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
_btn_click.layer.cornerRadius = 20 * WIDTHRADIU;
}
return _btn_click;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
//
// OnFootEarnCoinsViewController.h
// Open
//
// Created by 雷俊博 on 2019/8/29.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface OnFootEarnCoinsViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
//
// OnFootEarnCoinsViewController.m
// Open
//
// Created by 雷俊博 on 2019/8/29.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "OnFootEarnCoinsViewController.h"
#import "Constants.h"
#import <Masonry.h>
#import "UIColor+Hex.h"
#import "UIButton+ImageTitleSpacing.h"
#import "CQCountDownButton.h"
#import <UICountingLabel.h>
#import "MQGradientProgressView.h"
#import "OnFootAlertView.h"
//获取步数
//#import "HealthKitManager.h"
//#import <CoreMotion/CoreMotion.h>
@interface OnFootEarnCoinsViewController ()<CQCountDownButtonDataSource, CQCountDownButtonDelegate>
{
BOOL open;
}
@property (nonatomic, strong)UIView *backView;
@property (nonatomic, strong)UIScrollView *scrollView;
@property (nonatomic, strong)UIImageView *backImg;
@property (nonatomic, strong)UIImageView *img_ring;
@property (nonatomic, strong)UILabel *lb_stepNumberTitle;
@property (nonatomic, strong)UICountingLabel *lb_stepNumber;
@property (nonatomic, strong)CQCountDownButton *btn_refreshStepNumber;
@property (nonatomic, strong)UIView *whiteCardView;
@property (nonatomic, strong)UILabel *lb_startStep;
@property (nonatomic, strong)UILabel *lb_endStep;
@property (nonatomic, strong)UIImageView *img_person;
@property (nonatomic, strong)MQGradientProgressView *progressView;
@property (nonatomic, strong)UIImageView *img_coins;
@property (nonatomic, strong)UILabel *lb_message;
@property (nonatomic, strong)UIButton *btn_GetCoins;
@property (nonatomic, strong)UIButton *btn_Invite;
@property (nonatomic, strong)UILabel *lb_rule;
@property (nonatomic, assign)NSInteger currentStepCount;
//@property(nonatomic,strong)CMPedometer *pedometer;
@end
@implementation OnFootEarnCoinsViewController
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.scrollView];
[self setupUI];
[self getStepCount];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBar.translucent = NO;
self.navigationController.navigationBar.barTintColor = [UIColor colorWithHex:0x18aa6f];
[self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
self.navigationController.navigationBar.barTintColor = [UIColor colorWithHex:0x18aa6f];
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@""] forBarMetrics:UIBarMetricsDefault];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
}];
}
#pragma mark - CQCountDownButton DataSource
// 设置起始倒计时秒数
- (NSUInteger)startCountDownNumOfCountDownButton:(CQCountDownButton *)countDownButton {
return 60;
}
#pragma mark - CQCountDownButton Delegate
// 倒计时按钮点击回调
- (void)countDownButtonDidClick:(CQCountDownButton *)countDownButton {
[self.btn_refreshStepNumber startCountDown];
[self getStepCount];
}
// 倒计时开始时的回调
- (void)countDownButtonDidStartCountDown:(CQCountDownButton *)countDownButton {
NSLog(@"倒计时开始");
}
// 倒计时进行中的回调
- (void)countDownButtonDidInCountDown:(CQCountDownButton *)countDownButton withRestCountDownNum:(NSInteger)restCountDownNum {
NSString *title = [NSString stringWithFormat:@"(%ld)秒", (long)restCountDownNum];
[_btn_refreshStepNumber setImage:[UIImage imageNamed:@""] forState:UIControlStateNormal];
[self.btn_refreshStepNumber setTitle:title forState:UIControlStateNormal];
}
// 倒计时结束时的回调
- (void)countDownButtonDidEndCountDown:(CQCountDownButton *)countDownButton {
[self.btn_refreshStepNumber setTitle:@"刷新" forState:UIControlStateNormal];
[_btn_refreshStepNumber setImage:[UIImage imageNamed:@"btn_exercise_refresh"] forState:UIControlStateNormal];
NSLog(@"倒计时结束");
}
#pragma mark - event response
- (void)refreshBtnClick:(UIButton *)sender
{
if (open) {
[self getStepCount];
}else{
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}else{
NSLog(@"不能打开设置界面");
}
}
}
- (void)invitePeopleClick:(UIButton *)sender
{
// [self shareWebPageToPlatformType:UMSocialPlatformType_WechatSession];
[OnFootAlertView alertWithButtonClickedBlock:^{
}];
}
#pragma mark - setupUI
- (void)setupUI
{
//UIScrollView
UIImage *image = [UIImage imageNamed:@"img_exercise_ring"];
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
[self.btn_refreshStepNumber mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.backView).offset(20 * WIDTHRADIU + imageHeight * WIDTHRADIU - 20 * WIDTHRADIU);
make.centerX.equalTo(self.backView);
make.width.offset(80 * WIDTHRADIU);
make.height.offset(30 * WIDTHRADIU);
}];
[self.lb_rule mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.whiteCardView.mas_bottom);
make.left.offset(10 * WIDTHRADIU);
make.right.offset(-10 * WIDTHRADIU);
}];
[self.img_ring mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(20 * WIDTHRADIU);
make.centerX.equalTo(self.backImg);
make.width.offset(imageWidth * WIDTHRADIU);
make.height.offset(imageHeight * WIDTHRADIU);
}];
//backImg
[self.lb_stepNumberTitle mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(47 * WIDTHRADIU);
make.centerX.equalTo(self.img_ring);
make.height.offset(14 * WIDTHRADIU);
}];
[self.lb_stepNumber mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_stepNumberTitle.mas_bottom).offset(10 * WIDTHRADIU);
make.centerX.equalTo(self.lb_stepNumberTitle);
make.height.offset(40 * WIDTHRADIU);
}];
[self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(35 * WIDTHRADIU);
make.left.offset(15 * WIDTHRADIU);
make.right.offset(-15 * WIDTHRADIU);
}];
[self.img_coins mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.progressView.mas_bottom).offset(13 * WIDTHRADIU);
make.right.equalTo(self.progressView);
}];
[self.lb_endStep mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.img_coins);
make.right.equalTo(self.img_coins.mas_left).offset(-4);
make.height.offset(12 * WIDTHRADIU);
}];
[self.lb_startStep mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.img_coins);
make.left.equalTo(self.progressView);
make.height.offset(12 * WIDTHRADIU);
}];
[self.lb_message mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_startStep.mas_bottom).offset(10 * WIDTHRADIU);
make.left.equalTo(self.lb_startStep);
make.height.offset(14 * WIDTHRADIU);
}];
[self.btn_GetCoins mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.progressView);
make.top.equalTo(self.lb_message.mas_bottom).offset(30 * WIDTHRADIU);
make.height.offset(45 * WIDTHRADIU);
}];
[self.btn_Invite mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.progressView);
make.top.equalTo(self.btn_GetCoins.mas_bottom).offset(20 * WIDTHRADIU);
make.height.offset(45 * WIDTHRADIU);
}];
}
#pragma mark - prvate methods
//网页分享
- (void)shareWebPageToPlatformType:(UMSocialPlatformType)platformType
{
//创建分享消息对象
UMSocialMessageObject *messageObject = [UMSocialMessageObject messageObject];
//创建网页内容对象
NSString* thumbURL = UMS_THUMB_IMAGE;
UMShareWebpageObject *shareObject = [UMShareWebpageObject shareObjectWithTitle:UMS_Title descr:UMS_Text thumImage:thumbURL];
//设置网页地址
shareObject.webpageUrl = UMS_WebLink;
//分享消息对象设置分享内容对象
messageObject.shareObject = shareObject;
//调用分享接口
[[UMSocialManager defaultManager] shareToPlatform:platformType messageObject:messageObject currentViewController:self completion:^(id data, NSError *error) {
if (error) {
UMSocialLogInfo(@"************Share fail with error %@*********",error);
}else{
if ([data isKindOfClass:[UMSocialShareResponse class]]) {
UMSocialShareResponse *resp = data;
//分享结果消息
UMSocialLogInfo(@"response message is %@",resp.message);
//第三方原始返回的数据
UMSocialLogInfo(@"response originalResponse data is %@",resp.originalResponse);
}else{
UMSocialLogInfo(@"response data is %@",data);
}
}
[self alertWithError:error];
}];
}
- (void)alertWithError:(NSError *)error
{
NSString *result = nil;
if (!error) {
result = [NSString stringWithFormat:@"分享成功"];
}
else{
NSMutableString *str = [NSMutableString string];
if (error.userInfo) {
for (NSString *key in error.userInfo) {
[str appendFormat:@"%@ = %@\n", key, error.userInfo[key]];
}
}
if (error) {
result = [NSString stringWithFormat:@"Share fail with error code: %d\n%@",(int)error.code, str];
result = [NSString stringWithFormat:@"分享失败"];
}
else{
result = [NSString stringWithFormat:@"Share fail"];
}
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:result
delegate:nil
cancelButtonTitle:NSLocalizedString(@"确定", @"确定")
otherButtonTitles:nil];
[alert show];
}
- (void)popJumpAnimationView:(UIView *)sender {
CGFloat duration = 0.4f;
CGFloat height = 5.f;
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
CGFloat currentTy = sender.transform.ty;
animation.duration = duration;
animation.values = @[@(currentTy), @(currentTy - height/4), @(currentTy-height/4*2), @(currentTy-height/4*3), @(currentTy - height), @(currentTy-height/4*3), @(currentTy -height/4*2), @(currentTy - height/4), @(currentTy)];
animation.keyTimes = @[ @(0), @(0.025), @(0.085), @(0.2), @(0.5), @(0.8), @(0.915), @(0.975), @(1) ];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = MAXFLOAT;
animation.removedOnCompletion = NO;
[sender.layer addAnimation:animation forKey:@"kViewShakerAnimationKey"];
}
#pragma mark - 富文本部分字体飘灰
- (NSMutableAttributedString *)setupAttributeString:(NSString *)text highlightText:(NSString *)highlightText {
NSRange hightlightTextRange = [text rangeOfString:highlightText];
NSMutableAttributedString *attributeStr = [[NSMutableAttributedString alloc] initWithString:text];
if (hightlightTextRange.length > 0) {
[attributeStr addAttribute:NSForegroundColorAttributeName
value:[UIColor colorWithHex:0xee551a]
range:hightlightTextRange];
[attributeStr addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:13.0f * WIDTHRADIU] range:hightlightTextRange];
return attributeStr;
}else {
return [highlightText copy];
}
}
/**
* 获取矩形的渐变色的UIImage(此函数还不够完善)
*
* @param bounds UIImage的bounds
* @param colors 渐变色数组,可以设置两种颜色
* @param gradientType 渐变的方式:0--->从上到下 1--->从左到右
*
* @return 渐变色的UIImage
*/
- (UIImage*)gradientImageWithBounds:(CGRect)bounds andColors:(NSArray*)colors andGradientType:(int)gradientType{
NSMutableArray *ar = [NSMutableArray array];
for(UIColor *c in colors) {
[ar addObject:(id)c.CGColor];
}
UIGraphicsBeginImageContextWithOptions(bounds.size, YES, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
CGPoint start;
CGPoint end;
switch (gradientType) {
case 0:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(0.0, bounds.size.height);
break;
case 1:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(bounds.size.width, 0.0);
break;
}
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGContextRestoreGState(context);
CGColorSpaceRelease(colorSpace);
UIGraphicsEndImageContext();
return image;
}
#pragma mark =====横向、纵向移动===========
-(CABasicAnimation *)moveX:(float)time X:(NSNumber *)x
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];///.y的话就向下移动。
animation.toValue = x;
animation.duration = time;
animation.removedOnCompletion = NO;//yes的话,又返回原位置了。
animation.repeatCount = 1;
animation.fillMode = kCAFillModeForwards;
return animation;
}
#pragma mark - life cycle
//获取计步数
- (void)getStepCount
{
WeakSelf
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
// 开始日期
NSDate *startDate = [calendar dateFromComponents:components];
// 结束日期
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
/*
//判断记步功能
if ([CMPedometer isStepCountingAvailable]) {
[self.pedometer queryPedometerDataFromDate:startDate toDate:endDate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
if (error) {
NSLog(@">>>error====%@",error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.btn_GetCoins setTitle:@"开启权限,领今日金币" forState:UIControlStateNormal];
[weakSelf.btn_GetCoins setBackgroundImage:[self gradientImageWithBounds:CGRectMake(0, 0, ScreenWidth - 50 * WIDTHRADIU, 45 * WIDTHRADIU) andColors:@[(id)[UIColor colorWithHex:0xee551a] ,(id)[UIColor colorWithHex:0xf36d39]] andGradientType:1] forState:UIControlStateNormal];
weakSelf.btn_GetCoins.userInteractionEnabled = YES;
});
self->open = NO;
return ;
}else {
self->open = YES;
NSLog(@"步数====%@",pedometerData.numberOfSteps);
NSLog(@"距离====%@",pedometerData.distance);
NSInteger stepCount = pedometerData.numberOfSteps.integerValue;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.currentStepCount = stepCount;
[weakSelf.lb_stepNumber countFrom:weakSelf.lb_stepNumber.text.integerValue to:weakSelf.currentStepCount];
});
}
}];
}else{
NSLog(@"记步功能不可用");
}
*/
}
- (void)setCurrentStepCount:(NSInteger)currentStepCount
{
_currentStepCount = currentStepCount;
NSString *message = [NSString stringWithFormat:@"提示: 还差%ld步才能领取金币,赶快邀请好友助力吧!",_currentStepCount>=5000 ? 0 : (5000 - _currentStepCount)];
self.lb_message.attributedText = [self setupAttributeString:message highlightText:[NSString stringWithFormat:@"%ld",_currentStepCount>=5000 ? 0 : (5000 - _currentStepCount)]];
if (_currentStepCount >= 5000) {
[self.btn_GetCoins setTitle:@"立即领取金币" forState:UIControlStateNormal];
[self.btn_GetCoins setBackgroundImage:[self gradientImageWithBounds:CGRectMake(0, 0, ScreenWidth - 50 * WIDTHRADIU, 45 * WIDTHRADIU) andColors:@[(id)[UIColor colorWithHex:0xee551a] ,(id)[UIColor colorWithHex:0xf36d39]] andGradientType:1] forState:UIControlStateNormal];
self.btn_GetCoins.userInteractionEnabled = YES;
}else{
[self.btn_GetCoins setTitle:[NSString stringWithFormat:@"还差%ld步领取100金币",(5000 - _currentStepCount)] forState:UIControlStateNormal];
}
[self.lb_stepNumber countFrom:self.lb_stepNumber.text.integerValue to:_currentStepCount];
CGFloat percentage = _currentStepCount/5000.0;
self.progressView.progress = percentage <= 1 ? percentage : 1.0;
UIImage *image = [UIImage imageNamed:@"img_exercise_person"];
double dx = 15 * WIDTHRADIU + self.progressView.progress *self.progressView.bounds.size.width - image.size.width;
NSNumber *x = [NSNumber numberWithDouble:dx];
CABasicAnimation *animation = [self moveX:0.25 X:x];
[self.img_person.layer addAnimation:animation forKey:nil];
}
//- (CMPedometer *)pedometer
//{
// if (!_pedometer) {
// _pedometer = [[CMPedometer alloc]init];
// }
// return _pedometer;
//}
- (UIScrollView *)scrollView
{
if (!_scrollView) {
_scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight - cl_kNavigationBarHeight)];
_scrollView.backgroundColor = [UIColor whiteColor];
_scrollView.contentSize = CGSizeMake(ScreenWidth, ScreenHeight);
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
[_scrollView addSubview:self.backView];
}
return _scrollView;
}
- (UIView *)backView
{
if (!_backView) {
_backView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight * 1)];
_backView.backgroundColor = [UIColor whiteColor];
[_backView addSubview:self.backImg];
[_backView addSubview:self.btn_refreshStepNumber];
[_backView addSubview:self.whiteCardView];
[_backView addSubview:self.lb_rule];
}
return _backView;
}
- (UIImageView *)backImg
{
if (!_backImg) {
UIImage *image = [UIImage imageNamed:@"img_exercise_back"];
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
CGFloat imageScale = imageHeight/imageWidth;
_backImg = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_WIDTH *imageScale)];
_backImg.image = image;
[_backImg addSubview:self.img_ring];
}
return _backImg;
}
- (UIImageView *)img_ring
{
if (!_img_ring) {
_img_ring = [[UIImageView alloc]init];
_img_ring.image = [UIImage imageNamed:@"img_exercise_ring"];
[_img_ring addSubview:self.lb_stepNumberTitle];
[_img_ring addSubview:self.lb_stepNumber];
// [_img_ring addSubview:self.btn_refreshStepNumber];
}
return _img_ring;
}
//当前步数
- (UILabel *)lb_stepNumberTitle
{
if (!_lb_stepNumberTitle) {
_lb_stepNumberTitle = [[UILabel alloc]init];
_lb_stepNumberTitle.text = @"当前步数";
_lb_stepNumberTitle.textAlignment = NSTextAlignmentCenter;
_lb_stepNumberTitle.textColor = [UIColor whiteColor];
_lb_stepNumberTitle.font = [UIFont systemFontOfSize:14 * WIDTHRADIU];
}
return _lb_stepNumberTitle;
}
- (UICountingLabel *)lb_stepNumber
{
if (!_lb_stepNumber) {
_lb_stepNumber = [[UICountingLabel alloc]init];
_lb_stepNumber.method = UILabelCountingMethodEaseOut;
_lb_stepNumber.format = @"%d";
_lb_stepNumber.text = @"0";
_lb_stepNumber.textAlignment = NSTextAlignmentCenter;
_lb_stepNumber.textColor = [UIColor whiteColor];
_lb_stepNumber.font = [UIFont systemFontOfSize:40 * WIDTHRADIU];
}
return _lb_stepNumber;
}
- (CQCountDownButton *)btn_refreshStepNumber
{
if (!_btn_refreshStepNumber) {
_btn_refreshStepNumber = [CQCountDownButton buttonWithType:UIButtonTypeCustom];
[_btn_refreshStepNumber setTitle:@"刷新" forState:UIControlStateNormal];
[_btn_refreshStepNumber setImage:[UIImage imageNamed:@"btn_exercise_refresh"] forState:UIControlStateNormal];
[_btn_refreshStepNumber setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_btn_refreshStepNumber.titleLabel.font = [UIFont systemFontOfSize:14 * WIDTHRADIU];
[_btn_refreshStepNumber layoutButtonWithEdgeInsetsStyle:GLButtonEdgeInsetsStyleLeft imageTitleSpace:4];
_btn_refreshStepNumber.backgroundColor = [UIColor colorWithHex:0x0b904b];
_btn_refreshStepNumber.layer.cornerRadius = 15 * WIDTHRADIU;
// [_btn_refreshStepNumber addTarget:self action:@selector(refreshBtnClick:) forControlEvents:UIControlEventTouchUpInside];
_btn_refreshStepNumber.delegate = self;
_btn_refreshStepNumber.dataSource = self;
}
return _btn_refreshStepNumber;
}
- (UIView *)whiteCardView
{
if (!_whiteCardView) {
UIImage *image = [UIImage imageNamed:@"img_exercise_back"];
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
CGFloat imageScale = imageHeight/imageWidth;
_whiteCardView = [[UIView alloc]initWithFrame:CGRectMake(10 * WIDTHRADIU, ScreenWidth *imageScale - 60 * WIDTHRADIU, SCREEN_WIDTH - 20 * WIDTHRADIU, 250 * WIDTHRADIU)];
_whiteCardView.backgroundColor = [UIColor whiteColor];
_whiteCardView.layer.cornerRadius = 10 * WIDTHRADIU;
[_whiteCardView addSubview:self.img_person];
[_whiteCardView addSubview:self.progressView];
[_whiteCardView addSubview:self.lb_startStep];
[_whiteCardView addSubview:self.lb_endStep];
[_whiteCardView addSubview:self.img_coins];
[_whiteCardView addSubview:self.lb_message];
[_whiteCardView addSubview:self.btn_GetCoins];
[_whiteCardView addSubview:self.btn_Invite];
}
return _whiteCardView;
}
- (UILabel *)lb_startStep
{
if (!_lb_startStep) {
_lb_startStep = [[UILabel alloc]init];
_lb_startStep.text = @"0步";
_lb_startStep.textColor = [UIColor colorWithHex:0x4d4d4d];
_lb_startStep.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
}
return _lb_startStep;
}
- (UILabel *)lb_endStep
{
if (!_lb_endStep) {
_lb_endStep = [[UILabel alloc]init];
_lb_endStep.text = @"5000步";
_lb_endStep.textColor = [UIColor colorWithHex:0x4d4d4d];
_lb_endStep.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
}
return _lb_endStep;
}
- (UIImageView *)img_coins
{
if (!_img_coins) {
_img_coins = [[UIImageView alloc]init];
_img_coins.image = [UIImage imageNamed:@"img_exercise_coins"];
[self popJumpAnimationView:_img_coins];
}
return _img_coins;
}
- (MQGradientProgressView *)progressView
{
if (!_progressView) {
_progressView = [[MQGradientProgressView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth - 50 * WIDTHRADIU, 8 * WIDTHRADIU)];
// _progressView.center = self.view.center;
// progressView.colorArr = @[(id)MQRGBColor(59, 221, 255).CGColor,(id)MQRGBColor(34, 126, 239).CGColor];
_progressView.colorArr = @[(id)[UIColor colorWithHex:0xfda783].CGColor,(id)[UIColor colorWithHex:0xee551a].CGColor];
_progressView.progress = 0.0;
}
return _progressView;
}
- (UIImageView *)img_person
{
if (!_img_person) {
UIImage *image = [UIImage imageNamed:@"img_exercise_person"];
_img_person = [[UIImageView alloc]init];
_img_person.image = image;
_img_person.frame = CGRectMake(15 * WIDTHRADIU + self.progressView.progress *self.progressView.bounds.size.width - image.size.width, 35 - image.size.height - 5, 14 * WIDTHRADIU, 21 * WIDTHRADIU);
}
return _img_person;
}
- (UILabel *)lb_message
{
if (!_lb_message) {
_lb_message = [[UILabel alloc]init];
_lb_message.text = @"提示: 还差1800步才能领取金币,赶快邀请好友助力吧!";
_lb_message.textColor = [UIColor colorWithHex:0x808080];
_lb_message.font = [UIFont systemFontOfSize:13 * WIDTHRADIU];
_lb_message.attributedText = [self setupAttributeString:_lb_message.text highlightText:@"1800"];
}
return _lb_message;
}
- (UIButton *)btn_GetCoins
{
if (!_btn_GetCoins) {
_btn_GetCoins = [UIButton buttonWithType:UIButtonTypeCustom];
[_btn_GetCoins setTitle:@"还差1800步领100金币" forState:UIControlStateNormal];
[_btn_GetCoins setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_btn_GetCoins.titleLabel.font = [UIFont systemFontOfSize:15 * WIDTHRADIU];
_btn_GetCoins.backgroundColor = [UIColor colorWithHex:0xcccccc];
_btn_GetCoins.layer.cornerRadius = 22.5 * WIDTHRADIU;
self.btn_GetCoins.layer.masksToBounds = YES;
[_btn_GetCoins addTarget:self action:@selector(refreshBtnClick:) forControlEvents:UIControlEventTouchUpInside];
_btn_GetCoins.userInteractionEnabled = NO;
}
return _btn_GetCoins;
}
- (UIButton *)btn_Invite
{
if (!_btn_Invite) {
_btn_Invite = [UIButton buttonWithType:UIButtonTypeCustom];
[_btn_Invite setTitle:@"邀请好友助力,每次可得500步" forState:UIControlStateNormal];
[_btn_Invite setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_btn_Invite.layer.cornerRadius = 22.5 * WIDTHRADIU;
_btn_Invite.layer.masksToBounds = YES;
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(0, 0, ScreenWidth - 50 * WIDTHRADIU, 45 * WIDTHRADIU);
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 0);
gradientLayer.locations = @[@(0.5),@(1.0)];//渐变点
[gradientLayer setColors:@[(id)[[UIColor colorWithHex:0xee551a] CGColor],(id)[[UIColor colorWithHex:0xf36d39] CGColor]]];//渐变数组
[_btn_Invite.layer addSublayer:gradientLayer];
_btn_Invite.titleLabel.font = [UIFont systemFontOfSize:15 * WIDTHRADIU];
[_btn_Invite addTarget:self action:@selector(invitePeopleClick:) forControlEvents:UIControlEventTouchUpInside];
}
return _btn_Invite;
}
- (UILabel *)lb_rule
{
if (!_lb_rule) {
_lb_rule = [[UILabel alloc]init];
_lb_rule.textColor = [UIColor colorWithHex:0x808080];
_lb_rule.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
NSString * titlestr=@"活动规则:\n";
NSMutableParagraphStyle * paragraphstyle0 =[[NSMutableParagraphStyle alloc]init];
[paragraphstyle0 setLineSpacing:5];
paragraphstyle0.alignment = NSTextAlignmentLeft;
NSMutableAttributedString * attribustring0 =[[NSMutableAttributedString alloc]initWithString: titlestr attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:14 * WIDTHRADIU],NSForegroundColorAttributeName:[UIColor colorWithHex:0x808080],NSParagraphStyleAttributeName:paragraphstyle0}];
NSString *text = @"1、每日步数达到5000步,可领取100金币;\n2、每位好友助力可增加500步,截止时间为22:00;\n3、每日步数截止时间为22:00,之后的步数计算在次日。\n";
//行间距
NSMutableParagraphStyle * paragraphstyle =[[NSMutableParagraphStyle alloc]init];
[paragraphstyle setLineSpacing:8];
paragraphstyle.alignment = NSTextAlignmentLeft;
NSMutableAttributedString * attribustring =[[NSMutableAttributedString alloc]initWithString: text attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14*WIDTHRADIU],NSForegroundColorAttributeName:[UIColor colorWithHex:0x808080],NSParagraphStyleAttributeName:paragraphstyle}];
//合并
[attribustring0 appendAttributedString:attribustring];
_lb_rule.attributedText = attribustring0;
_lb_rule.numberOfLines =0;
}
return _lb_rule;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
//
// CQCountDownButton.h
// CQCountDownButton
//
// Created by CaiQiang on 2017/9/8.
// Copyright © 2017年 caiqiang. All rights reserved.
//
// Repo Detail: https://github.com/CaiWanFeng/CQCountDownButton
// About Author: https://www.jianshu.com/u/4212f351f6b5
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class CQCountDownButton;
@protocol CQCountDownButtonDataSource <NSObject>
@required
// 设置起始倒计时秒数
- (NSUInteger)startCountDownNumOfCountDownButton:(CQCountDownButton *)countDownButton;
@end
@protocol CQCountDownButtonDelegate <NSObject>
@optional
// 倒计时按钮点击回调
- (void)countDownButtonDidClick:(CQCountDownButton *)countDownButton;
// 倒计时开始时的回调
- (void)countDownButtonDidStartCountDown:(CQCountDownButton *)countDownButton;
@required
// 倒计时进行中的回调
- (void)countDownButtonDidInCountDown:(CQCountDownButton *)countDownButton withRestCountDownNum:(NSInteger)restCountDownNum;
// 倒计时结束时的回调
- (void)countDownButtonDidEndCountDown:(CQCountDownButton *)countDownButton;
@end
@interface CQCountDownButton : UIButton
#pragma mark - block版本
/**
所有回调通过block配置
@param duration 设置起始倒计时秒数
@param buttonClicked 倒计时按钮点击回调
@param countDownStart 倒计时开始时的回调
@param countDownUnderway 倒计时进行中的回调
@param countDownCompletion 倒计时结束时的回调
*/
- (void)configDuration:(NSUInteger)duration
buttonClicked:(nullable dispatch_block_t)buttonClicked
countDownStart:(nullable dispatch_block_t)countDownStart
countDownUnderway:(void (^)(NSInteger restCountDownNum))countDownUnderway
countDownCompletion:(dispatch_block_t)countDownCompletion;
#pragma mark - delegate版本
@property (nonatomic, weak, nullable) id <CQCountDownButtonDataSource> dataSource;
@property (nonatomic, weak, nullable) id <CQCountDownButtonDelegate> delegate;
#pragma mark - 开始/结束 倒计时
/**
开始倒计时
*/
- (void)startCountDown;
/**
结束倒计时
倒计时结束时会自动调用此方法
也可以主动调用此方法提前结束倒计时
调用此方法会回调倒计时结束的block和代理方法
*/
- (void)endCountDown;
@end
NS_ASSUME_NONNULL_END
//
// CQCountDownButton.m
// CQCountDownButton
//
// Created by CaiQiang on 2017/9/8.
// Copyright © 2017年 caiqiang. All rights reserved.
//
// Repo Detail: https://github.com/CaiWanFeng/CQCountDownButton
// About Author: https://www.jianshu.com/u/4212f351f6b5
//
#import "CQCountDownButton.h"
#import "NSTimer+CQBlockSupport.h"
@interface CQCountDownButton ()
/** 控制倒计时的timer */
@property (nonatomic, strong) NSTimer *timer;
/** 倒计时按钮点击回调 */
@property (nonatomic, copy) dispatch_block_t buttonClickedBlock;
/** 倒计时开始时的回调 */
@property (nonatomic, copy) dispatch_block_t countDownStartBlock;
/** 倒计时进行中的回调 */
@property (nonatomic, copy) void (^countDownUnderwayBlock)(NSInteger restCountDownNum);
/** 倒计时结束时的回调 */
@property (nonatomic, copy) dispatch_block_t countDownCompletionBlock;
@end
@implementation CQCountDownButton {
/** 倒计时开始值 */
NSInteger _startCountDownNum;
/** 剩余倒计时的值 */
NSInteger _restCountDownNum;
// 使用block(若使用block则不能再使用delegate,反之亦然)
BOOL _useBlock;
// 数据源响应的方法
struct {
BOOL startCountDownNumOfCountDownButton:TRUE;
} _dataSourceRespondsTo;
// 代理响应的方法
struct {
BOOL countDownButtonDidClick:TRUE;
BOOL countDownButtonDidStartCountDown:TRUE;
BOOL countDownButtonDidInCountDown:TRUE;
BOOL countDownButtonDidEndCountDown:TRUE;
} _delegateRespondsTo;
}
#pragma mark - init
// 纯代码init和initWithFrame方法调用
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addTarget:self action:@selector(p_buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
// xib和storyboard调用
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self addTarget:self action:@selector(p_buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
#pragma mark - dealloc
// 检验是否释放
- (void)dealloc {
NSLog(@"倒计时按钮已释放");
}
#pragma mark - block版本
/**
所有回调通过block配置
@param duration 设置起始倒计时秒数
@param buttonClicked 倒计时按钮点击回调
@param countDownStart 倒计时开始时的回调
@param countDownUnderway 倒计时进行中的回调
@param countDownCompletion 倒计时结束时的回调
*/
- (void)configDuration:(NSUInteger)duration
buttonClicked:(nullable dispatch_block_t)buttonClicked
countDownStart:(nullable dispatch_block_t)countDownStart
countDownUnderway:(void (^)(NSInteger restCountDownNum))countDownUnderway
countDownCompletion:(dispatch_block_t)countDownCompletion {
if (_dataSource || _delegate) {
[self p_showError];
return;
}
_useBlock = YES;
_startCountDownNum = duration;
self.buttonClickedBlock = buttonClicked;
self.countDownStartBlock = countDownStart;
self.countDownUnderwayBlock = countDownUnderway;
self.countDownCompletionBlock = countDownCompletion;
}
#pragma mark - delegate版本
- (void)setDataSource:(id<CQCountDownButtonDataSource>)dataSource {
if (_useBlock) {
[self p_showError];
return;
}
if (_dataSource != dataSource) {
_dataSource = dataSource;
// 缓存数据源方法是否可以调用
_dataSourceRespondsTo.startCountDownNumOfCountDownButton = [_dataSource respondsToSelector:@selector(startCountDownNumOfCountDownButton:)];
}
_startCountDownNum = [_dataSource startCountDownNumOfCountDownButton:self];
}
- (void)setDelegate:(id<CQCountDownButtonDelegate>)delegate {
if (_useBlock) {
[self p_showError];
return;
}
if (_delegate != delegate) {
_delegate = delegate;
// 缓存代理方法是否可以调用
_delegateRespondsTo.countDownButtonDidClick = [_delegate respondsToSelector:@selector(countDownButtonDidClick:)];
_delegateRespondsTo.countDownButtonDidStartCountDown = [_delegate respondsToSelector:@selector(countDownButtonDidStartCountDown:)];
_delegateRespondsTo.countDownButtonDidInCountDown = [_delegate respondsToSelector:@selector(countDownButtonDidInCountDown:withRestCountDownNum:)];
_delegateRespondsTo.countDownButtonDidEndCountDown = [_delegate respondsToSelector:@selector(countDownButtonDidEndCountDown:)];
}
}
#pragma mark - 开始/结束 倒计时
/** 开始倒计时 */
- (void)startCountDown {
// 因为可以主动调用此方法开启倒计时,不需要点击按钮,所以此处需要将enabled设为no
self.enabled = NO;
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
_restCountDownNum = _startCountDownNum;
// 倒计时开始的回调
!self.countDownStartBlock ?: self.countDownStartBlock();
if (_delegateRespondsTo.countDownButtonDidStartCountDown) {
[_delegate countDownButtonDidStartCountDown:self];
}
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer cq_scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer *timer) {
__strong typeof(self) strongSelf = weakSelf;
[strongSelf p_handleCountDown];
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
self.timer.fireDate = [NSDate distantPast];
}
/** 结束倒计时 */
- (void)endCountDown {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
// 重置剩余倒计时
_restCountDownNum = _startCountDownNum;
// 倒计时结束的回调
!self.countDownCompletionBlock ?: self.countDownCompletionBlock();
if (_delegateRespondsTo.countDownButtonDidEndCountDown) {
[_delegate countDownButtonDidEndCountDown:self];
}
// 恢复按钮的enabled状态
self.enabled = YES;
}
#pragma mark - 事件处理
/** 按钮点击 */
- (void)p_buttonClicked:(CQCountDownButton *)sender {
sender.enabled = NO;
!self.buttonClickedBlock ?: self.buttonClickedBlock();
if (_delegateRespondsTo.countDownButtonDidClick) {
[_delegate countDownButtonDidClick:self];
}
}
/** 处理倒计时进行中的事件 */
- (void)p_handleCountDown {
// 调用倒计时进行中的回调
!self.countDownUnderwayBlock ?: self.countDownUnderwayBlock(_restCountDownNum);
if (_delegateRespondsTo.countDownButtonDidInCountDown) {
[_delegate countDownButtonDidInCountDown:self withRestCountDownNum:_restCountDownNum];
}
if (_restCountDownNum == 0) { // 倒计时完成
[self endCountDown];
return;
}
_restCountDownNum --;
}
#pragma mark - 提示错误
- (void)p_showError {
NSAssert(nil, @"不能同时使用block和delegate");
}
@end
//
// NSTimer+CQBlockSupport.h
// CQCountDownButton
//
// Created by caiqiang on 2018/12/5.
// Copyright © 2018年 caiqiang. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSTimer (CQBlockSupport)
+ (NSTimer *)cq_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
@end
//
// NSTimer+CQBlockSupport.m
// CQCountDownButton
//
// Created by caiqiang on 2018/12/5.
// Copyright © 2018年 caiqiang. All rights reserved.
//
#import "NSTimer+CQBlockSupport.h"
@implementation NSTimer (CQBlockSupport)
+ (NSTimer *)cq_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block {
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(cq_callBlock:) userInfo:[block copy] repeats:repeats];
}
+ (void)cq_callBlock:(NSTimer *)timer {
void (^block)(NSTimer *timer) = timer.userInfo;
!block ?: block(timer);
}
@end
//
// HealthKitManager.m
// Open
//
// Created by 雷俊博 on 2019/8/30.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#define HKVersion [[[UIDevice currentDevice] systemVersion] doubleValue]
#define CustomHealthErrorDomain @"com.sdqt.healthError"
#import "HealthKitManager.h"
@implementation HealthKitManager
+(id)shareInstance{
static id manager ;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[[self class] alloc] init];
});
return manager;
}
/*
* @brief 检查是否支持获取健康数据
*/
- (void)authorizeHealthKit:(void(^)(BOOL success, NSError *error))compltion
{
if(HKVersion >= 8.0)
{
if (![HKHealthStore isHealthDataAvailable]) {
NSError *error = [NSError errorWithDomain: @"com.raywenderlich.tutorials.healthkit" code: 2 userInfo: [NSDictionary dictionaryWithObject:@"HealthKit is not available in th is Device" forKey:NSLocalizedDescriptionKey]];
if (compltion != nil) {
compltion(false, error);
}
return;
}
if ([HKHealthStore isHealthDataAvailable]) {
if(self.healthStore == nil)
self.healthStore = [[HKHealthStore alloc] init];
/*
组装需要读写的数据类型
*/
// NSSet *writeDataTypes = [self dataTypesToWrite];
NSSet *readDataTypes = [self dataTypesRead];
/*
注册需要读写的数据类型,也可以在“健康”APP中重新修改
*/
[self.healthStore requestAuthorizationToShareTypes:nil readTypes:readDataTypes completion:^(BOOL success, NSError *error) {
if (compltion != nil) {
NSLog(@"error->%@", error.localizedDescription);
compltion (success, error);
}
}];
}
}
else {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey];
NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
compltion(0,aError);
}
}
/*!
* @brief 写权限
* @return 集合
*/
- (NSSet *)dataTypesToWrite
{
HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
HKQuantityType *temperatureType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];
HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
return [NSSet setWithObjects:heightType, temperatureType, weightType,activeEnergyType,nil];
}
/*!
* @brief 读权限
* @return 集合
*/
- (NSSet *)dataTypesRead
{
// HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
// HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
// HKQuantityType *temperatureType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];
// HKCharacteristicType *birthdayType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];
// HKCharacteristicType *sexType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
// HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
// HKQuantityType *distance = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
// HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
//
// return [NSSet setWithObjects:heightType, temperatureType,birthdayType,sexType,weightType,stepCountType, distance, activeEnergyType,nil];
HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
// HKQuantityType *distance = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
return [NSSet setWithObjects:stepCountType,nil];
}
//获取步数
- (void)getStepCount:(void(^)(double value, NSError *error))completion
{
HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
// Since we are interested in retrieving the user's latest sample, we sort the samples in descending order, and set the limit to 1. We are not filtering the data, and so the predicate is set to nil.
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:stepType predicate:[HealthKitManager predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if(error)
{
completion(0,error);
}
else
{
NSInteger totleSteps = 0;
for(HKQuantitySample *quantitySample in results)
{
HKQuantity *quantity = quantitySample.quantity;
HKUnit *heightUnit = [HKUnit countUnit];
double usersHeight = [quantity doubleValueForUnit:heightUnit];
totleSteps += usersHeight;
}
NSLog(@"当天行走步数 = %ld",(long)totleSteps);
completion(totleSteps,error);
}
}];
[self.healthStore executeQuery:query];
}
//获取公里数
- (void)getDistance:(void(^)(double value, NSError *error))completion
{
HKQuantityType *distanceType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:distanceType predicate:[HealthKitManager predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
if(error)
{
completion(0,error);
}
else
{
double totleSteps = 0;
for(HKQuantitySample *quantitySample in results)
{
HKQuantity *quantity = quantitySample.quantity;
HKUnit *distanceUnit = [HKUnit meterUnitWithMetricPrefix:HKMetricPrefixKilo];
double usersHeight = [quantity doubleValueForUnit:distanceUnit];
totleSteps += usersHeight;
}
NSLog(@"当天行走距离 = %.2f",totleSteps);
completion(totleSteps,error);
}
}];
[self.healthStore executeQuery:query];
}
/*!
* @brief 当天时间段
*
* @return 时间段
*/
+ (NSPredicate *)predicateForSamplesToday {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
[components setHour:0];
[components setMinute:0];
[components setSecond: 0];
NSDate *startDate = [calendar dateFromComponents:components];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
return predicate;
}
@end
//
// MQGradientProgressView.h
// MQGradientProgress
//
// Created by 小马 on 2017/7/24.
// Copyright © 2017年 maqi. All rights reserved.
//
#import <UIKit/UIKit.h>
#define MQRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
@interface MQGradientProgressView : UIView
/**
* 进度条背景颜色 默认是 (230, 244, 245)
*/
@property (nonatomic, strong) UIColor *bgProgressColor;
/**
* 进度条渐变颜色数组,颜色个数>=2
* 默认是 @[(id)MQRGBColor(252, 244, 77).CGColor,(id)MQRGBColor(252, 93, 59).CGColor]
*/
@property (nonatomic, strong) NSArray *colorArr;
/**
* 进度 默认是0.65
*/
@property (nonatomic, assign) CGFloat progress;
- (void)refreshUI;
@end
//
// MQGradientProgressView.m
// MQGradientProgress
//
// Created by 小马 on 2017/7/24.
// Copyright © 2017年 maqi. All rights reserved.
//
#import "MQGradientProgressView.h"
#import "Constants.h"
@interface MQGradientProgressView ()
@property (nonatomic, strong) CALayer *bgLayer;
@property (nonatomic, strong) CAGradientLayer *gradientLayer;
@end
@implementation MQGradientProgressView
#pragma mark -
#pragma mark - GET ---> view
- (CALayer *)bgLayer {
if (!_bgLayer) {
_bgLayer = [CALayer layer];
//一般不用frame,因为不支持隐式动画
_bgLayer.bounds = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
_bgLayer.anchorPoint = CGPointMake(0, 0);
_bgLayer.backgroundColor = self.bgProgressColor.CGColor;
_bgLayer.cornerRadius = self.frame.size.height / 2.;
[self.layer addSublayer:_bgLayer];
}
return _bgLayer;
}
- (CAGradientLayer *)gradientLayer {
if (!_gradientLayer) {
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.bounds = CGRectMake(0, 0, self.frame.size.width * self.progress, self.frame.size.height);
_gradientLayer.startPoint = CGPointMake(0, 0);
_gradientLayer.endPoint = CGPointMake(1, 0);
_gradientLayer.anchorPoint = CGPointMake(0, 0);
NSArray *colorArr = self.colorArr;
_gradientLayer.colors = colorArr;
_gradientLayer.cornerRadius = self.frame.size.height / 2.;
[self.layer addSublayer:_gradientLayer];
}
return _gradientLayer;
}
#pragma mark -
#pragma mark - SET ---> data
- (void)setProgress:(CGFloat)progress {
_progress = progress;
[self updateView];
}
- (void)setColorArr:(NSArray *)colorArr {
if (colorArr.count >= 2) {
_colorArr = colorArr;
[self updateView];
}else {
NSLog(@">>>>>颜色数组个数小于2,显示默认颜色");
}
}
#pragma mark -
#pragma mark - init
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self config];
[self simulateViewDidLoad];
self.colorArr = @[(id)MQRGBColor(252, 244, 77).CGColor,(id)MQRGBColor(252, 93, 59).CGColor];
self.progress = 0.65;
}
return self;
}
- (void)simulateViewDidLoad {
[self addSubViewTree];
}
- (void)config {
self.bgProgressColor = MQRGBColor(230., 230., 230.);
}
- (void)addSubViewTree {
[self bgLayer];
[self gradientLayer];
}
- (void)updateView {
self.gradientLayer.frame = CGRectMake(0, 0, self.frame.size.width * self.progress, 8 * WIDTHRADIU);
self.gradientLayer.colors = self.colorArr;
}
@end
//
// CTPrizeDetailProcessTableViewCell.h
// Open
//
// Created by 雷俊博 on 2019/9/6.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ListPrizeTableViewCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface CTPrizeDetailProcessTableViewCell : UITableViewCell
@property (assign, nonatomic) BOOL hasUpLine;
@property (assign, nonatomic) BOOL hasDownLine;
@property (assign, nonatomic) BOOL currented;
- (void)reloadUIWithData:(NSDictionary *)dic state:(CTListPrizeState)state Index:(NSInteger)index;
@end
NS_ASSUME_NONNULL_END
//
// CTPrizeDetailProcessTableViewCell.m
// Open
//
// Created by 雷俊博 on 2019/9/6.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "CTPrizeDetailProcessTableViewCell.h"
#import "CTPrizeDetailProcessTableCellContentView.h"
@interface CTPrizeDetailProcessTableViewCell ()
@property (nonatomic, strong)CTPrizeDetailProcessTableCellContentView *customView;
@end
@implementation CTPrizeDetailProcessTableViewCell
#pragma mark life cycle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self setupUI];
}
return self;
}
- (void)setupUI {
CTPrizeDetailProcessTableCellContentView *custom = [[CTPrizeDetailProcessTableCellContentView alloc]init];
custom.backgroundColor=[UIColor clearColor];
[self addSubview:custom];
self.customView = custom;
custom.currented = NO;
custom.hasUpLine = NO;
custom.hasDownLine = NO;
[custom mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self);
}];
}
#pragma mark - public methods
- (void)reloadUIWithData:(NSDictionary *)dic state:(CTListPrizeState)state Index:(NSInteger)index
{
[self.customView reloadUIWithData:dic state:state Index:index];
}
#pragma mark - gettrs and setters
- (void)setHasUpLine:(BOOL)hasUpLine {
self.customView.hasUpLine = hasUpLine;
}
- (void)setHasDownLine:(BOOL)hasDownLine {
self.customView.hasDownLine = hasDownLine;
}
- (void)setCurrented:(BOOL)currented {
self.customView.currented = currented;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end
//
// SNXUXIANView.h
// Open
//
// Created by 雷俊博 on 2019/9/5.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface SNXUXIANView : UIView
@end
NS_ASSUME_NONNULL_END
//
// SNXUXIANView.m
// Open
//
// Created by 雷俊博 on 2019/9/5.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "SNXUXIANView.h"
#import "UIColor+Hex.h"
@implementation SNXUXIANView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initViews];
}
return self;
}
- (void)initViews
{
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context=UIGraphicsGetCurrentContext();//获取绘图用的图形上下文
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);//填充色设置成
CGFloat lengths[] = {4};
CGContextSetLineDash(context, 4, lengths,1);
CGContextFillRect(context,self.bounds);//把整个空间用刚设置的颜色填充
//上面是准备工作,下面开始画线了
CGContextSetStrokeColorWithColor(context, [UIColor colorWithHex:0xcccccc].CGColor);//设置线的颜色
CGContextMoveToPoint(context,0,0);//画线的起始点位置
CGContextAddLineToPoint(context,self.frame.size.width,0);//画第一条线的终点位置
CGContextStrokePath(context);//把线在界面上绘制出来
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
//
// ListPrizeTableViewCell.h
// Open
//
// Created by 雷俊博 on 2019/9/3.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, CTListPrizeType) {
CTListPrizeTypePackage2,
CTListPrizeTypePackage3,
CTListPrizeTypePackage10,
CTListPrizeTypeCoins200,
CTListPrizeTypeCoins388,
CTListPrizeTypeCoins588,
CTListPrizeTypeUnKnow
};
typedef NS_ENUM(NSUInteger, CTListPrizeState) {
CTListPrizeStateUnGet,//未领取
CTListPrizeStateReceived,//已领取
CTListPrizeStateGiving,//发放中
CTListPrizeStateInvalid,//已过期
CTListPrizeStateFailure,//发放失败
CTListPrizeStateUnKnow
};
@interface ListPrizeTableViewCell : UITableViewCell
@property (nonatomic, assign)CTListPrizeType type;
@property (nonatomic, assign)CTListPrizeState state;
- (void)refreshUIWithData:(NSDictionary *)dic;
@end
NS_ASSUME_NONNULL_END
//
// ListPrizeTableViewCell.m
// Open
//
// Created by 雷俊博 on 2019/9/3.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "ListPrizeTableViewCell.h"
#import "UIColor+Hex.h"
#import "NSString+AttributedString.h"
#import "UIButton+ImageTitleSpacing.h"
#import "Constants.h"
#import <Masonry.h>
@interface ListPrizeTableViewCell ()
@property (nonatomic, strong)UIView *backView;
@property (nonatomic, strong)UIImageView *img_package;
//红色背景图片还是灰色背景图片
@property (nonatomic, strong)UIImageView *img_state;
@property (nonatomic, strong)UIImageView *lineView;
//金币或红包
@property (nonatomic, strong)UILabel *lb_type;
//金币数
@property (nonatomic, strong)UILabel *lb_number;
@property (nonatomic, strong)UILabel *lb_unit;
@property (nonatomic, strong)UILabel *lb_bottomType;
@property (nonatomic, strong)UILabel *lb_content;
//@property (nonatomic, strong)
@property (nonatomic, strong)UILabel *lb_date;
@property (nonatomic, strong)UIImageView *img_failure;
@property (nonatomic, strong)UILabel *lb_account;
@property (nonatomic, strong)UIImageView *img_rightState;
@property (nonatomic, strong)UIButton *btn_detail;
@property (nonatomic, strong)UIImageView *img_listState;
@property(nonatomic,strong)MASConstraint *lb_numberConstraint;
//unknow
@property (nonatomic, strong)UILabel *lb_download1;
@property (nonatomic, strong)UILabel *lb_download2;
@end
@implementation ListPrizeTableViewCell
#pragma mark - life cycle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView addSubview:self.backView];
[self.contentView addSubview:self.img_state];
[self.contentView addSubview:self.lb_content];
[self.contentView addSubview:self.lb_date];
[self.contentView addSubview:self.img_failure];
[self.contentView addSubview:self.lb_account];
[self.contentView addSubview:self.lb_download1];
[self.contentView addSubview:self.lb_download2];
[self.contentView addSubview:self.img_listState];
self.backgroundColor = [UIColor clearColor];
[self setupUI];
}
return self;
}
- (void)setupUI
{
[self.backView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.right.offset(0);
make.left.offset(20 * WIDTHRADIU);
}];
[self.img_state mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.offset(0);
make.width.offset(120 * WIDTHRADIU);
make.height.offset(85 * WIDTHRADIU);
}];
[self.lb_type mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.img_state);
make.left.offset(7 * WIDTHRADIU);
}];
[self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.img_state);
make.left.offset(24 * WIDTHRADIU);
}];
WeakSelf
[self.lb_number mas_makeConstraints:^(MASConstraintMaker *make) {
weakSelf.lb_numberConstraint = make.centerX.equalTo(self.img_state).offset(12 * WIDTHRADIU - 9 * WIDTHRADIU);
make.top.offset(16 * WIDTHRADIU);
make.height.offset(35 * WIDTHRADIU);
}];
[self.lb_unit mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.lb_number.mas_right).offset(5 * WIDTHRADIU);
make.bottom.equalTo(self.lb_number);
make.height.offset(11 * WIDTHRADIU);
}];
[self.lb_bottomType mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_number.mas_bottom).offset(10 * WIDTHRADIU);
make.centerX.equalTo(self.img_state).offset(12 * WIDTHRADIU);
make.height.offset(12 * WIDTHRADIU);
}];
[self.lb_content mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.img_state.mas_right).offset(15 * WIDTHRADIU);
make.top.offset(15 * WIDTHRADIU);
make.height.offset(16 * WIDTHRADIU);
}];
[self.lb_download1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.img_state.mas_right).offset(15 * WIDTHRADIU);
make.top.offset(15 * WIDTHRADIU);
}];
[self.lb_download2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_download1.mas_bottom).offset(8 * WIDTHRADIU);
make.left.equalTo(self.lb_download1);
make.height.offset(12 * WIDTHRADIU);
}];
[self.lb_date mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_content.mas_bottom).offset(12 * WIDTHRADIU);
make.left.equalTo(self.lb_content);
make.height.offset(12 * WIDTHRADIU);
}];
[self.img_failure mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_date.mas_bottom).offset(8 * WIDTHRADIU);
make.left.equalTo(self.lb_content);
}];
[self.lb_account mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.lb_date.mas_bottom).offset(8 * WIDTHRADIU);
make.left.equalTo(self.img_failure.mas_right);
make.height.offset(12 * WIDTHRADIU);
}];
[self.img_rightState mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.top.offset(0);
make.width.height.offset(50 * WIDTHRADIU);
}];
[self.btn_detail mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.offset(-5 * WIDTHRADIU);
make.bottom.offset(-7 * WIDTHRADIU);
}];
[self.img_listState mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.offset(-20 * WIDTHRADIU);
make.top.offset(10 * WIDTHRADIU);
}];
[self.img_package mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.img_state);
}];
}
#pragma mark public methods
- (void)refreshUIWithData:(NSDictionary *)dic
{
NSString *giftId = dic[@"giftId"];
if (![NSString blankString:dic[@"createTime"]]) {
NSDate *startDate = [self getDateWithDateStr:dic[@"createTime"]];
NSDate *endDate = [self getDateAfter30DaysWithCurrentDate:startDate];
self.lb_date.text = [NSString stringWithFormat:@"有效期:%@-%@",[self getCustomDateStrWithDate:startDate],[self getCustomDateStrWithDate:endDate]];
}
if ([giftId isEqualToString:@"1"]){
self.type = CTListPrizeTypeCoins200;
}else if ([giftId isEqualToString:@"9"]) {
self.type = CTListPrizeTypePackage10;
}else if ([giftId isEqualToString:@"10"]){
self.type = CTListPrizeTypePackage3;
}else if ([giftId isEqualToString:@"11"]){
self.type = CTListPrizeTypePackage2;
}else if ([giftId isEqualToString:@"12"]){
self.type = CTListPrizeTypeCoins388;
}else if ([giftId isEqualToString:@"13"]){
self.type = CTListPrizeTypeCoins588;
}else{
self.type = CTListPrizeTypeUnKnow;
}
if ([dic[@"lotterStatus"] isEqualToString:@"未领取"]) {
self.state = CTListPrizeStateUnGet;
}else if ([dic[@"lotterStatus"] isEqualToString:@"发放中"]){
self.state = CTListPrizeStateGiving;
}else if ([dic[@"lotterStatus"] isEqualToString:@"已过期"]){
self.state = CTListPrizeStateInvalid;
}else if ([dic[@"lotterStatus"] isEqualToString:@"已领取"]){
self.state = CTListPrizeStateReceived;
}else if ([dic[@"lotterStatus"] isEqualToString:@"领取失败"]){
self.state = CTListPrizeStateFailure;
}
[self.lb_numberConstraint uninstall];
switch (self.type) {
case CTListPrizeTypeCoins200:
case CTListPrizeTypeCoins388:
case CTListPrizeTypeCoins588:
{
//奖品类型
NSString *str = @"金\n币";
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:str];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 8.0 * WIDTHRADIU; // 设置行间距
paragraphStyle.alignment = NSTextAlignmentCenter; //设置两端对齐显示
[attributedStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedStr.length)];
self.lb_type.attributedText = attributedStr;
self.lineView.hidden = NO;
//隐藏单位标签
self.lb_unit.hidden = YES;
//金币数量
self.lb_number.text = [dic[@"giftName"] stringByReplacingOccurrencesOfString:@"金币" withString:@""];
//奖品类型2
self.lb_bottomType.text = @"金币奖励";
//奖品名称
self.lb_content.text = [NSString stringWithFormat:@"%@",dic[@"giftName"]];
self.lb_content.hidden = NO;
self.lb_date.hidden = NO;
self.lb_account.hidden = NO;
self.btn_detail.hidden = NO;
self.img_listState.hidden = NO;
self.img_rightState.hidden = NO;
self.lb_download1.hidden = YES;
self.lb_download2.hidden = YES;
//金币时数字的位置在虚线后面部分居中
[self.lb_number mas_makeConstraints:^(MASConstraintMaker *make) {
self.lb_numberConstraint = make.centerX.equalTo(self.img_state).offset(12 * WIDTHRADIU);
}];
switch (self.state) {
case CTListPrizeStateUnGet:
{
self.lb_account.text = @"未领取";
}
break;
case CTListPrizeStateGiving:
{
self.lb_account.text = @"发放中";
}
break;
case CTListPrizeStateReceived:
{
self.lb_account.text = @"发放成功";
if (![NSString blankString:dic[@"createTime"]]) {
NSDate *startDate = [self getDateWithDateStr:dic[@"createTime"]];
NSDate *endDate = [self getDateAfter30DaysWithCurrentDate:startDate];
self.lb_date.text = [NSString stringWithFormat:@"领取时间:%@",[self getCustomDateStrWithDate:startDate]];
}
}
break;
case CTListPrizeStateInvalid:
{
self.lb_account.text = @"已过期";
}
break;
case CTListPrizeStateFailure:
{
self.lb_account.text = @"发放失败";
}
break;
default:
break;
}
}
break;
case CTListPrizeTypePackage2:
case CTListPrizeTypePackage3:
case CTListPrizeTypePackage10:
{
//奖品类型
NSString *str = @"红\n包";
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:str];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 8.0 * WIDTHRADIU; // 设置行间距
paragraphStyle.alignment = NSTextAlignmentCenter; //设置两端对齐显示
[attributedStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedStr.length)];
self.lb_type.attributedText = attributedStr;
self.lb_unit.hidden = NO;
self.lineView.hidden = NO;
self.lb_number.text = [dic[@"giftName"] stringByReplacingOccurrencesOfString:@"元红包" withString:@""];
self.lb_bottomType.text = @"微信红包";
self.lb_content.text = [NSString stringWithFormat:@"%@元微信红包",self.lb_number.text];
self.lb_content.hidden = NO;
self.lb_date.hidden = NO;
self.lb_account.hidden = NO;
self.btn_detail.hidden = NO;
self.img_listState.hidden = NO;
self.img_rightState.hidden = NO;
self.lb_download1.hidden = YES;
self.lb_download2.hidden = YES;
[self.lb_number mas_makeConstraints:^(MASConstraintMaker *make) {
self.lb_numberConstraint = make.centerX.equalTo(self.img_state).offset(12 * WIDTHRADIU - 9 * WIDTHRADIU);
}];
switch (self.state) {
case CTListPrizeStateUnGet:
{
NSString *payOrderNo = dic[@"payOrderNo"];
self.lb_account.text = [NSString blankString:payOrderNo] ? @"未领取" : [NSString stringWithFormat:@"提现账号:%@",payOrderNo];
}
break;
case CTListPrizeStateGiving:
{
NSString *payOrderNo = dic[@"payOrderNo"];
self.lb_account.text = [NSString blankString:payOrderNo] ? @"未领取" : [NSString stringWithFormat:@"提现账号:%@",payOrderNo];
}
break;
case CTListPrizeStateReceived:
{
self.lb_account.text = @"发放成功";
if (![NSString blankString:dic[@"createTime"]]) {
NSDate *startDate = [self getDateWithDateStr:dic[@"createTime"]];
NSDate *endDate = [self getDateAfter30DaysWithCurrentDate:startDate];
self.lb_date.text = [NSString stringWithFormat:@"领取时间:%@",[self getCustomDateStrWithDate:startDate]];
}
}
break;
case CTListPrizeStateInvalid:
{
self.lb_account.text = @"已过期";
}
break;
case CTListPrizeStateFailure:
{
self.lb_account.text = @"发放失败";
}
break;
default:
break;
}
}
break;
case CTListPrizeTypeUnKnow:
{
NSString *str = @"";
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:str];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 8.0 * WIDTHRADIU; // 设置行间距
paragraphStyle.alignment = NSTextAlignmentCenter; //设置两端对齐显示
[attributedStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedStr.length)];
self.lb_type.attributedText = attributedStr;
self.lb_unit.hidden = YES;
self.lineView.hidden = YES;
self.lb_number.text = @"";
self.lb_bottomType.text = @"";
self.lb_content.hidden = YES;
self.lb_date.hidden = YES;
self.lb_account.hidden = YES;
self.btn_detail.hidden = YES;
self.img_rightState.hidden = YES;
self.img_listState.hidden = YES;
self.lb_download1.hidden = NO;
self.lb_download2.hidden = NO;
[self.lb_number mas_makeConstraints:^(MASConstraintMaker *make) {
self.lb_numberConstraint = make.centerX.equalTo(self.img_state).offset(12 * WIDTHRADIU);
}];
self.img_state.image = [UIImage imageNamed:@"奖品-框-未发放"];
}
break;
default:
break;
}
}
#pragma mark - 字符串转日期后30天后的日期
- (NSDate *)getDateWithDateStr:(NSString *)str
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy/MM/dd"];
NSDate *date = [dateFormatter dateFromString:str];
return date;
}
- (NSDate *)getDateAfter30DaysWithCurrentDate:(NSDate *)date
{
NSDate *newDate = [date dateByAddingTimeInterval:30 * 60 * 60 * 24];
return newDate;
}
- (NSString *)getCustomDateStrWithDate:(NSDate *)date
{
// 获取代表公历的NSCalendar对象
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
// 定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息
unsigned unitFlags = NSCalendarUnitYear |
NSCalendarUnitMonth | NSCalendarUnitDay |
NSCalendarUnitHour | NSCalendarUnitMinute |
NSCalendarUnitSecond | NSCalendarUnitWeekday;
// 获取不同时间字段的信息
NSDateComponents* comp = [gregorian components: unitFlags
fromDate:date];
// 获取各时间字段的数值
NSLog(@"现在是%ld年" , comp.year);
NSLog(@"现在是%ld月 " , comp.month);
NSLog(@"现在是%ld日" , comp.day);
NSLog(@"现在是%ld时" , comp.hour);
NSLog(@"现在是%ld分" , comp.minute);
NSLog(@"现在是%ld秒" , comp.second);
NSLog(@"现在是星期%ld" , comp.weekday);
return [NSString stringWithFormat:@"%ld.%ld.%ld",(long)comp.year,comp.month,comp.day];
}
#pragma mark - getters and setters
- (void)setType:(CTListPrizeType)type
{
_type = type;
switch (type) {
case CTListPrizeTypeCoins200:
case CTListPrizeTypeCoins388:
case CTListPrizeTypeCoins588:
{
self.img_package.image = [UIImage imageNamed:@""];
}
break;
case CTListPrizeTypePackage2:
case CTListPrizeTypePackage3:
case CTListPrizeTypePackage10:
{
self.img_package.image = [UIImage imageNamed:@""];
}
break;
case CTListPrizeTypeUnKnow:
{
self.img_package.image = [UIImage imageNamed:@"神秘礼包"];
}
break;
default:
break;
}
}
- (void)setState:(CTListPrizeState)state
{
_state = state;
switch (state) {
case CTListPrizeStateUnGet:
{
self.img_state.image = [UIImage imageNamed:@"奖品-框-未发放"];
self.img_rightState.image = [UIImage imageNamed:@"未领取"];
self.img_listState.image = [UIImage imageNamed:@""];
self.img_failure.image = [UIImage imageNamed:@""];
self.lb_content.textColor = [UIColor colorWithHex:0x808080];
self.lb_date.textColor = [UIColor colorWithHex:0x808080];
self.lb_account.textColor = [UIColor colorWithHex:0x808080];
}
break;
case CTListPrizeStateGiving:
{
self.img_state.image = [UIImage imageNamed:@"奖品-框-未发放"];
self.img_rightState.image = [UIImage imageNamed:@"发放中"];
self.img_listState.image = [UIImage imageNamed:@""];
self.img_failure.image = [UIImage imageNamed:@""];
self.lb_content.textColor = [UIColor colorWithHex:0x808080];
self.lb_date.textColor = [UIColor colorWithHex:0x808080];
self.lb_account.textColor = [UIColor colorWithHex:0x808080];
}
break;
case CTListPrizeStateReceived:
{
self.img_state.image = [UIImage imageNamed:@"奖品-框-已发放"];
self.img_rightState.image = [UIImage imageNamed:@""];
self.img_listState.image = [UIImage imageNamed:@"my_activity_prize_Rogerthat_normal"];
self.img_failure.image = [UIImage imageNamed:@""];
self.lb_content.textColor = [UIColor colorWithHex:0xcccccc];
self.lb_date.textColor = [UIColor colorWithHex:0xcccccc];
self.lb_account.textColor = [UIColor colorWithHex:0xcccccc];
}
break;
case CTListPrizeStateInvalid:
{
self.img_state.image = [UIImage imageNamed:@"奖品-框-已发放"];
self.img_rightState.image = [UIImage imageNamed:@""];
self.img_listState.image = [UIImage imageNamed:@"my_activity_prize_Invalid_normal"];
self.img_failure.image = [UIImage imageNamed:@""];
self.lb_content.textColor = [UIColor colorWithHex:0xcccccc];
self.lb_date.textColor = [UIColor colorWithHex:0xcccccc];
self.lb_account.textColor = [UIColor colorWithHex:0xcccccc];
}
break;
case CTListPrizeStateFailure:
{
self.img_state.image = [UIImage imageNamed:@"奖品-框-发放失败"];
self.img_rightState.image = [UIImage imageNamed:@"发放失败"];
self.img_listState.image = [UIImage imageNamed:@""];
self.img_failure.image = [UIImage imageNamed:@"警示"];
self.lb_content.textColor = [UIColor colorWithHex:0xf19011];
self.lb_date.textColor = [UIColor colorWithHex:0xf19011];
self.lb_account.textColor = [UIColor colorWithHex:0xf19011];
}
default:
break;
}
}
- (UIView *)backView
{
if (!_backView) {
_backView = [[UIView alloc]init];
_backView.backgroundColor = [UIColor whiteColor];
_backView.layer.cornerRadius = 5;
// 阴影颜色
_backView.layer.shadowColor = [UIColor colorWithHex:0x797979].CGColor;
// 阴影偏移,默认(0, -3)
_backView.layer.shadowOffset = CGSizeMake(0,0);
// 阴影透明度,默认0
_backView.layer.shadowOpacity = 0.5;
// 阴影半径,默认3
_backView.layer.shadowRadius = 2;
[_backView addSubview:self.img_rightState];
[_backView addSubview:self.btn_detail];
}
return _backView;
}
- (UIImageView *)img_package
{
if (!_img_package) {
_img_package = [[UIImageView alloc]init];
}
return _img_package;
}
- (UIImageView *)img_state
{
if (!_img_state) {
_img_state = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 120 * WIDTHRADIU, 85 * WIDTHRADIU)];
_img_state.image = [UIImage imageNamed:@"奖品-框-未发放"];
[_img_state addSubview:self.lb_type];
[_img_state addSubview:self.lineView];
[_img_state addSubview:self.lb_number];
[_img_state addSubview:self.lb_unit];
[_img_state addSubview:self.lb_bottomType];
[_img_state addSubview:self.img_package];
}
return _img_state;
}
- (UILabel *)lb_type
{
if (!_lb_type) {
_lb_type = [[UILabel alloc]init];
_lb_type.textAlignment = NSTextAlignmentCenter;
_lb_type.textColor = [UIColor whiteColor];
_lb_type.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
_lb_type.numberOfLines = 0;
// NSString *str = @"红\n包";
// NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:str];
// NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
// paragraphStyle.lineSpacing = 8.0 * WIDTHRADIU; // 设置行间距
// paragraphStyle.alignment = NSTextAlignmentCenter; //设置两端对齐显示
// [attributedStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedStr.length)];
// _lb_type.attributedText = attributedStr;
}
return _lb_type;
}
- (UIImageView *)lineView
{
if (!_lineView) {
_lineView = [[UIImageView alloc]init];
_lineView.image = [UIImage imageNamed:@"分割线"];
}
return _lineView;
}
- (UILabel *)lb_number
{
if (!_lb_number) {
_lb_number = [[UILabel alloc]init];
_lb_number.textColor = [UIColor whiteColor];
_lb_number.textAlignment = NSTextAlignmentCenter;
_lb_number.font = [UIFont systemFontOfSize:45 * WIDTHRADIU];
//_lb_number.text = @"10";
}
return _lb_number;
}
- (UILabel *)lb_unit
{
if (!_lb_unit) {
_lb_unit = [[UILabel alloc]init];
_lb_unit.textColor = [UIColor whiteColor];
_lb_unit.textAlignment = NSTextAlignmentCenter;
_lb_unit.font = [UIFont systemFontOfSize:13 * WIDTHRADIU];
_lb_unit.text = @"元";
}
return _lb_unit;
}
- (UILabel *)lb_bottomType
{
if (!_lb_bottomType) {
_lb_bottomType = [[UILabel alloc]init];
_lb_bottomType.textAlignment = NSTextAlignmentCenter;
_lb_bottomType.textColor = [UIColor whiteColor];
_lb_bottomType.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
// _lb_bottomType.text = @"微信红包";
}
return _lb_bottomType;
}
- (UILabel *)lb_content
{
if (!_lb_content) {
_lb_content = [[UILabel alloc]init];
//_lb_content.text = @"2元微信红包";
_lb_content.textColor = [UIColor colorWithHex:0x808080];
_lb_content.font = [UIFont boldSystemFontOfSize:16 * WIDTHRADIU];
}
return _lb_content;
}
- (UILabel *)lb_date
{
if (!_lb_date) {
_lb_date = [[UILabel alloc]init];
//_lb_date.text = @"有效期:2019.09.02-2019.10.02";
_lb_date.textColor = [UIColor colorWithHex:0x808080];
_lb_date.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
}
return _lb_date;
}
- (UIImageView *)img_failure
{
if (!_img_failure) {
_img_failure = [[UIImageView alloc]init];
}
return _img_failure;
}
- (UILabel *)lb_account
{
if (!_lb_account) {
_lb_account = [[UILabel alloc]init];
//_lb_account.text = @"未绑定微信";
_lb_account.textColor = [UIColor colorWithHex:0x808080];
_lb_account.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
}
return _lb_account;
}
- (UIImageView *)img_rightState
{
if (!_img_rightState) {
_img_rightState = [[UIImageView alloc]init];
//_img_rightState.image = [UIImage imageNamed:@"未领取"];
}
return _img_rightState;
}
- (UIButton *)btn_detail
{
if (!_btn_detail) {
_btn_detail = [UIButton buttonWithType:UIButtonTypeCustom];
[_btn_detail setTitle:@"详情" forState:UIControlStateNormal];
[_btn_detail setTitleColor:[UIColor colorWithHex:0xb2b2b2] forState:UIControlStateNormal];
[_btn_detail setImage:[UIImage imageNamed:@"my_activity_prize_Details_normal"] forState:UIControlStateNormal];
_btn_detail.titleLabel.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
[_btn_detail layoutButtonWithEdgeInsetsStyle:GLButtonEdgeInsetsStyleRight imageTitleSpace:5];
}
return _btn_detail;
}
- (UIImageView *)img_listState
{ if (!_img_listState) {
_img_listState = [[UIImageView alloc]init];
_img_listState.image = [UIImage imageNamed:@"my_activity_prize_Rogerthat_normal"];
}
return _img_listState;
}
- (UILabel *)lb_download1
{
if (!_lb_download1) {
_lb_download1 = [[UILabel alloc]init];
_lb_download1.text = @"下载最新版“看看社保”\n才能查看礼包";
_lb_download1.textColor = [UIColor colorWithHex:0x808080];
_lb_download1.font = [UIFont boldSystemFontOfSize:16 * WIDTHRADIU];
_lb_download1.numberOfLines = 0;
}
return _lb_download1;
}
- (UILabel *)lb_download2
{
if (!_lb_download2) {
_lb_download2 = [[UILabel alloc]init];
_lb_download2.text = @"“我的”-“设置”-“关于”-“版本更新”";
_lb_download2.textColor = [UIColor colorWithHex:0x808080];
_lb_download2.font = [UIFont systemFontOfSize:12 * WIDTHRADIU];
}
return _lb_download2;
}
- (void)setFrame:(CGRect)frame{
frame.origin.x += 15 * WIDTHRADIU;
frame.origin.y += 15 * WIDTHRADIU;
frame.size.height -= 15 * WIDTHRADIU;
frame.size.width -= 30 * WIDTHRADIU;
[super setFrame:frame];
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end
//
// NoDataView.h
// Open
//
// Created by 雷俊博 on 2019/9/5.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface NoDataView : UIView
@end
NS_ASSUME_NONNULL_END
//
// NoDataView.m
// Open
//
// Created by 雷俊博 on 2019/9/5.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "NoDataView.h"
#import "UIColor+Hex.h"
#import "Constants.h"
#import <Masonry.h>
@interface NoDataView ()
@property (nonatomic,strong)UIImageView *img_icon;
@property (nonatomic, strong)UILabel *lb_message;
@end
@implementation NoDataView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.img_icon];
[self addSubview:self.lb_message];
[self setupUI];
}
return self;
}
- (void)setupUI
{
[self.img_icon mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self);
make.top.offset(116 * WIDTHRADIU);
}];
[self.lb_message mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self);
make.top.equalTo(self.img_icon.mas_bottom).offset(14 * WIDTHRADIU);
}];
}
#pragma mark - getters and setters
- (UIImageView *)img_icon
{
if (!_img_icon) {
_img_icon = [[UIImageView alloc]init];
_img_icon.image = [UIImage imageNamed:@"我的奖品-空状态"];
}
return _img_icon;
}
- (UILabel *)lb_message
{
if (!_lb_message) {
_lb_message = [[UILabel alloc]init];
_lb_message.numberOfLines = 0;
_lb_message.font = [UIFont systemFontOfSize:15 * WIDTHRADIU];
_lb_message.textColor = [UIColor colorWithHex:0xbfbfbf];
NSString *str = @"暂无奖品,快去抽奖中心\n赢取更多礼品吧!";
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:str];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 10.0 * WIDTHRADIU; // 设置行间距
paragraphStyle.alignment = NSTextAlignmentCenter; //设置两端对齐显示
[attributedStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedStr.length)];
_lb_message.attributedText = attributedStr;
}
return _lb_message;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
//
// MiddleBannerCollectionViewCell.h
// Open
//
// Created by 雷俊博 on 2019/8/22.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MiddleBannerCollectionViewCell : UICollectionViewCell
@property (nonatomic, strong)UIImageView *goodsImageView;
- (void)configureCellWithInfo:(NSDictionary *)data;
@end
NS_ASSUME_NONNULL_END
//
// MiddleBannerCollectionViewCell.m
// Open
//
// Created by 雷俊博 on 2019/8/22.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "MiddleBannerCollectionViewCell.h"
#import "Constants.h"
#import "UIColor+Hex.h"
@interface MiddleBannerCollectionViewCell ()
@end
@implementation MiddleBannerCollectionViewCell
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self.contentView addSubview:self.goodsImageView];
[self setupUI];
}
return self;
}
- (void)setupUI
{
[self.goodsImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.offset(0);
make.top.bottom.offset(0);
}];
}
#pragma mark - public methods
- (void)configureCellWithInfo:(NSDictionary *)data
{
}
#pragma mark - getters and setters
- (UIImageView *)goodsImageView
{
if (!_goodsImageView) {
_goodsImageView = [[UIImageView alloc]init];
}
return _goodsImageView;
}
@end
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "btn_exercise_refresh.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "btn_exercise_refresh@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "btn_exercise_refresh@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_back.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_back@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_back@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_coins.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_coins@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_coins@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_person.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_person@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_person@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_ring.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_ring@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_ring@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_back.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_back@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_back@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_coins.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_coins@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_coins@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_person.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_person@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_person@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "img_exercise_ring.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_ring@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "img_exercise_ring@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
......@@ -2,17 +2,17 @@
"images" : [
{
"idiom" : "universal",
"filename" : "发现.png",
"filename" : "Find-n.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "发现@2x.png",
"filename" : "Find-n@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "发现@3x.png",
"filename" : "Find-n@3x.png",
"scale" : "3x"
}
],
......
......@@ -2,17 +2,17 @@
"images" : [
{
"idiom" : "universal",
"filename" : "发现_s.png",
"filename" : "Find-s.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "发现_s@2x.png",
"filename" : "Find-s@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "发现_s@3x.png",
"filename" : "Find-s@3x.png",
"scale" : "3x"
}
],
......
{
"images" : [
{
"idiom" : "universal",
"filename" : "middleBanner.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "middleBanner@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "middleBanner@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "TencentTask.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "TencentTask@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "TencentTask@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "coins-signSuccess.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "coins-signSuccess@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "coins-signSuccess@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
//
// BannerView.h
// 轮播图(循环和自动)
//
// Created by 裴铎 on 2018/3/9.
// Copyright © 2018年 裴铎. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
7./创建一个协议
需要把文件的名称传过来
用@class BannerView;只传名称
*/
@class PDBannerView;
//定义一个协议( @protocol 协议名称 <NSObject> )
@protocol PDBannerViewDelegate <NSObject>
/**
协议方法实现方式
optional 可选 (默认是必须实现的)
*/
@optional
/**
8./用来传值的协议方法
@param bannerView 本类
@param currentImage 传进来的当前的图片
*/
- (void)selectImage:(PDBannerView *)bannerView currentImage:(NSInteger)currentImage;
@end//协议结束
@interface PDBannerView : UIView
/**
9./创建一个代理属性(用 weak)
写在.h文件里的属性 外界可以调用
*/
@property (nonatomic , weak) id <PDBannerViewDelegate> delegate;
/**
销毁定时器方法
*/
+ (void)destroyTimer;
/**
1./声明一个自定义的构造方法 让外界的对象用来初始化bannerView
@param frame 外界传入的frame
@param addImageArray 外界传入的图片数组
@return 1
*/
- (id)initWithFrame:(CGRect)frame andImageArray:(NSMutableArray *)addImageArray;
@end
//
// BannerView.m
// 轮播图(循环和自动)
//
// Created by 裴铎 on 2018/3/9.
// Copyright © 2018年 裴铎. All rights reserved.
//
#import "PDBannerView.h"
#import "UIColor+Hex.h"
/**
定时器 用来自动播放图片
*/
static NSTimer * mv_timer;
/**
定义私有变量
遵守滚动式图的代理方法 实现拖拽效果
*/
@interface PDBannerView () <
UIScrollViewDelegate>{
//banNerView的宽和高 私有成员变量用下划线开头(书写习惯)
CGFloat mv_width;
CGFloat mv_height;
}
/**
分页控件
*/
@property (nonatomic , strong) UIPageControl * mainPage;
/**
scrollView
*/
@property (nonatomic , strong) UIScrollView * mainScrollView;
/**
图片数组
*/
@property (nonatomic , strong) NSMutableArray * dataArray;
@end
@implementation PDBannerView
//- (instancetype)initWithFrame:(CGRect)frame{
//
// self = [super initWithFrame:frame];
//
// if (self) {
//
// //系统的初始化方法
// }
//
// return self;
//}
/**
2./自定义的init构造方法 在.h文件提前声明
@param frame 外界初始化时传入的frame 带有bannerView的宽和高
@param addImageArray 传入的图片数组
@return 1
*/
- (id)initWithFrame:(CGRect)frame andImageArray:(NSMutableArray *)addImageArray{
//调用父类方法
self = [super initWithFrame:frame];
//判断是否是本类对象调用 并 外界传入的图片数量足够滚动
if (self && addImageArray.count > 2) {
/**
获取banNerView 的宽度
宽和高是外界传入的 frame (只能在这个方法内有效)
所以需要定义一个本类都能使用的成员变量
*/
mv_width = frame.size.width;
//或取bannerView 的高度
mv_height = frame.size.height;
//图片数组 1 2 3 4 5 6 把外界传入的图片数组赋值给本类的数组
self.dataArray = [NSMutableArray arrayWithArray:addImageArray];
//在数组的最后一位添加传进来的第一张图片 1 2 3 4 5 6 1
[self.dataArray addObject:addImageArray.firstObject];
/**
在数组的第一位添加传进来的最后一张图片 6 1 2 3 4 5 6 1
insert 插入元素 atIndex: 根据下标
*/
[self.dataArray insertObject:addImageArray.lastObject atIndex:0];
//初始化时把mainscrollView 加载到banNerView上
[self addSubview:self.mainScrollView];
//初始化时把分页控件加载到bannerView中
[self addSubview:self.mainPage];
//初始化时加载定时器
[self addTimer];
}
/**
返回本类
当外界用本类的初始化方法时
返回一个视图 bannerView 给外界
*/
return self;
}
/**
滚动视图开始手动拖拽时出发
*/
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
//判断是否有定时器
if (mv_timer) {
//如果有定时器就暂停定时器(两种方法实现暂停效果)
/**
NSTimer 自带的方法中没有暂停和继续定时器的方法
但是有一个setFireDate:方法 (定时器的触发时间)
原理是把定时器的触发时间设置成很久的将来
这样定时器就会进入等待触发的状态 (实现暂停效果)
distantFuture(遥远的未来)
*/
[mv_timer setFireDate:[NSDate distantFuture]];
/**
用NSTimer自带的停止定时器的方法 invalidate
这个方法会吧定时器永久停止,无法再次启用
所以需要把定时器清空 nil
当需要再次开启定时器时 重新初始化定时器
[self.timer invalidate];
self.timer = nil;
*/
}
}
/**
滚动视图正在滚动 (拖拽过程中触发的方法)
*/
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
self.mainPage.currentPage = scrollView.contentOffset.x / mv_width - 1;
}
/**
滚动视图完成减速时调用 (就是手动拖拽完成后)
*/
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
//判断是否有定时器
if (mv_timer) {
/**
设置定时器的触发时间
延后2秒触发
*/
[mv_timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
/**
重新初始化定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerFUNC:) userInfo:nil repeats:YES];
*/
}
//获取当前滚动视图的偏移量
CGPoint currentPoint = scrollView.contentOffset;
/** 判断拖拽完成后将要显示的图片时第几张 6 1 2 3 4 5 6 1 */
//如果是数组内的最后一张图片 1
if (currentPoint.x == (self.dataArray.count - 1) * mv_width) {
//改变偏移量 显示数组内的第一张图片 1
scrollView.contentOffset = CGPointMake(mv_width, 0);
}
//如果是数组内的第一张图片 6
if (currentPoint.x == 0) {
//改变偏移量 显示数组内的 第二个图片6
scrollView.contentOffset = CGPointMake((self.dataArray.count - 2) * mv_width, 0);
}
/**
如果是图片数组的第一张图片 或 最后一张图片时
滚动视图的偏移量发生了改变
所以之前的偏移量变量不能再使用了 (获取一个新的偏移量)
*/
//获取新的滚佛那个视图偏移量
CGPoint newPoint = scrollView.contentOffset;
//改变分页控件上的页码
self.mainPage.currentPage = newPoint.x / mv_width - 1;
}
/**
5./初始化定时器
*/
- (void)addTimer{
//初始化定时器 时间戳:2.0秒 目标:本类 方法选择器:timerFUNC 用户信息:nil 是否循环:yes
mv_timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(timerFUNC:) userInfo:nil repeats:YES];
/**
将定时器添加到当前线程中(currentRunLoop 当前线程)
[NSRunLoop currentRunLoop]可以的到一个当前线程下的NSRunLoop对象
addTimer:添加一个定时器
forMode:什么模式
NSRunLoopCommonModes 共同模式
*/
[[NSRunLoop currentRunLoop] addTimer:mv_timer forMode:NSRunLoopCommonModes];
/**
在开启一个NSTimer实质上是在当前的runloop中注册了一个新的事件源,
而当scrollView滚动的时候,当前的MainRunLoop是处于UITrackingRunLoopMode的模式下,
在这个模式下,是不会处理NSDefaultRunLoopMode的消息(因为RunLoop 的 Mode不一样),
要想在scrollView滚动的同时也接受其它runloop的消息,我们需要改变两者之间的runLoopMode.
简单的说就是NSTimer不会开启新的进程,只是在RunLoop里注册了一下,
RunLoop每次loop时都会检测这个timer,看是否可以触发。
当Runloop在A mode,而timer注册在B mode时就无法去检测这个timer,
所以需要把NSTimer也注册到A mode,这样就可以被检测到。
所以模式参数 forMode: 填写 NSRunLoopCommonModes 共同模式
*/
}
/**
6./实现定时器方法
*/
- (void)timerFUNC:(NSTimer *)timer{
/**
获取当前图片的X位置
也就是定时器再次出发时滚动视图上正在显示的是哪一张图片
*/
CGFloat currentX = self.mainScrollView.contentOffset.x;
/**
获取下一张图片的X位置
当前位置 + 一个屏幕宽度
*/
CGFloat nextX = currentX + mv_width;
/**
判断滚动视图上将要显示的图片是最后一张时
通过X值来判断 所以要 self.dataArray.count - 1
*/
if (nextX == (self.dataArray.count - 1) * mv_width) {
/**
UIView的动画效果方法(分两个方法)
*/
[UIView animateWithDuration:0.2 animations:^{
/**
动画效果的第一个方法
Duration:持续时间
animations:动画内容
这个动画执行 0.2秒 后进入下一个方法
*/
//往最后一张图片走
self.mainScrollView.contentOffset = CGPointMake(nextX, 0);
/**
改变对应的分页控件显示圆点
*/
self.mainPage.currentPage = 0;
} completion:^(BOOL finished) {
/**
动画效果的第二个方法
completion: 回调方法 (完成\结束的意思)
上一个方法结束后进入这个方法
*/
//往第二张图片走
self.mainScrollView.contentOffset = CGPointMake(self->mv_width, 0);
}];
}else{//如果滚动视图上要显示的图片不是最后一张时
//显示下一张图片
[UIView animateWithDuration:0.2 animations:^{
//让下一个图片显示出来
self.mainScrollView.contentOffset = CGPointMake( nextX, 0);
//改变对应的分页控件显示圆点
self.mainPage.currentPage = self.mainScrollView.contentOffset.x / self->mv_width - 1;
} completion:^(BOOL finished) {
//改变对应的分页控件显示圆点
self.mainPage.currentPage = self.mainScrollView.contentOffset.x / self->mv_width - 1;
}];
}
}
/**
4./加载分页控件
*/
- (UIPageControl *)mainPage{
if (!_mainPage) {
//初始化分页控制器
_mainPage = [[UIPageControl alloc]initWithFrame:CGRectMake( 50, mv_height - 20, mv_width - 50 * 2, 20)];
//分页控件上要显示的圆点数量
_mainPage.numberOfPages = self.dataArray.count - 2;
//分页控件不允许和用户交互(不许点击)
_mainPage.userInteractionEnabled = NO;
//设置 默认点 的颜色
_mainPage.pageIndicatorTintColor = [UIColor colorWithHex:0xffffff alpha:0.5];
//设置 滑动点(当前点) 的颜色
_mainPage.currentPageIndicatorTintColor = [UIColor whiteColor];
}
return _mainPage;
}
/**
3./加载滚动视图
*/
- (UIScrollView *)mainScrollView{
if (!_mainScrollView) {
//初始化滚动控件
_mainScrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, mv_width, mv_height)];
//滚动式图的代理
_mainScrollView.delegate = self;
/**
滚动范围(手动拖拽时的范围)
如果不写就不能手动拖拽(但是定时器可以让图片滚动)
*/
_mainScrollView.contentSize = CGSizeMake(self.dataArray.count * mv_width, mv_height);
//分页滚动效果 yes
_mainScrollView.pagingEnabled = YES;
//能否滚动
_mainScrollView.scrollEnabled = YES;
//弹簧效果 NO
_mainScrollView.bounces = NO;
//滚动视图的起始偏移量
_mainScrollView.contentOffset = CGPointMake(mv_width, 0);
//垂直滚动条
_mainScrollView.showsVerticalScrollIndicator = NO;
//水平滚动条
_mainScrollView.showsHorizontalScrollIndicator = NO;
/**
循环往滚动视图上添加图片视图
循环条件 i < self.dataArray.count 一定不要写等号 =
如果 i <= self.dataArray.count 程序就会崩溃,(下标越界)
*/
for (int i = 0; i < self.dataArray.count; i ++) {
//初始化图片视图
UIImageView * imgV = [[UIImageView alloc]initWithFrame:CGRectMake(mv_width * i, 0, mv_width, mv_height)];
//给图片视图添加图片 通过图片数组
imgV.image = [UIImage imageNamed:self.dataArray[i]];
//让图片可以与用户交互
imgV.userInteractionEnabled = YES;
//初始化一个点击手势
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAcyion:)];
//把点击手势添加到图片上
[imgV addGestureRecognizer:tap];
//把图片视图 添加 到滚动视图上
[_mainScrollView addSubview:imgV];
}
}
//返回滚动视图 给 bannerView
return _mainScrollView;
}
/**
点击图片触发的手势方法
*/
- (void)tapAcyion:(UITapGestureRecognizer *)tap{
/**
如果代理属性能够响应协议方法方法
才会通过代理属性 调用协议方法
*/
if ([self.delegate respondsToSelector:@selector(selectImage:currentImage:)]) {
/**
通过代理属性 调用协议方法
currentImage:当前的图片时第几张
可以通过分页控件的当前圆点来判断是第几张图片
*/
[self.delegate selectImage:self currentImage:self.mainPage.currentPage];
}
}
+ (void)destroyTimer{
// 清理定时器
[mv_timer invalidate];
mv_timer = nil;
}
@end
../../../Reachability/Reachability.h
\ No newline at end of file
../../../Toast/Toast-Framework/Toast.h
\ No newline at end of file
../../../Toast/Toast/UIView+Toast.h
\ No newline at end of file
../../../UICountingLabel/UICountingLabel.h
\ No newline at end of file
../../../Reachability/Reachability.h
\ No newline at end of file
../../../Toast/Toast-Framework/Toast.h
\ No newline at end of file
../../../Toast/Toast/UIView+Toast.h
\ No newline at end of file
../../../UICountingLabel/UICountingLabel.h
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9F174662D84916BF4F9443D75BCB2946"
BuildableName = "libReachability.a"
BlueprintName = "Reachability"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97F970E97EB414C65EE9F58D5B909D10"
BuildableName = "libToast.a"
BlueprintName = "Toast"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0F088CBCD1A14E113BC154F4100AC585"
BuildableName = "libUICountingLabel.a"
BlueprintName = "UICountingLabel"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Copyright (c) 2011-2013, Tony Million.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
[![Reference Status](https://www.versioneye.com/objective-c/reachability/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/reachability/references)
# Reachability
This is a drop-in replacement for Apple's `Reachability` class. It is ARC-compatible, and it uses the new GCD methods to notify of network interface changes.
In addition to the standard `NSNotification`, it supports the use of blocks for when the network becomes reachable and unreachable.
Finally, you can specify whether a WWAN connection is considered "reachable".
*DO NOT OPEN BUGS UNTIL YOU HAVE TESTED ON DEVICE*
## Requirements
Once you have added the `.h/m` files to your project, simply:
* Go to the `Project->TARGETS->Build Phases->Link Binary With Libraries`.
* Press the plus in the lower left of the list.
* Add `SystemConfiguration.framework`.
Boom, you're done.
## Examples
### Block Example
This sample uses blocks to notify when the interface state has changed. The blocks will be called on a **BACKGROUND THREAD**, so you need to dispatch UI updates onto the main thread.
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:@"www.google.com"];
// Set the blocks
reach.reachableBlock = ^(Reachability*reach)
{
// keep in mind this is called on a background thread
// and if you are updating the UI it needs to happen
// on the main thread, like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"REACHABLE!");
});
};
reach.unreachableBlock = ^(Reachability*reach)
{
NSLog(@"UNREACHABLE!");
};
// Start the notifier, which will cause the reachability object to retain itself!
[reach startNotifier];
### `NSNotification` Example
This sample will use `NSNotification`s to notify when the interface has changed. They will be delivered on the **MAIN THREAD**, so you *can* do UI updates from within the function.
In addition, it asks the `Reachability` object to consider the WWAN (3G/EDGE/CDMA) as a non-reachable connection (you might use this if you are writing a video streaming app, for example, to save the user's data plan).
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:@"www.google.com"];
// Tell the reachability that we DON'T want to be reachable on 3G/EDGE/CDMA
reach.reachableOnWWAN = NO;
// Here we set up a NSNotification observer. The Reachability that caused the notification
// is passed in the object parameter
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
[reach startNotifier];
## Tell the world
Head over to [Projects using Reachability](https://github.com/tonymillion/Reachability/wiki/Projects-using-Reachability) and add your project for "Maximum Wins!".
/*
Copyright (c) 2011, Tony Million.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
/**
* Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
*
* @see http://nshipster.com/ns_enum-ns_options/
**/
#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif
extern NSString *const kReachabilityChangedNotification;
typedef NS_ENUM(NSInteger, NetworkStatus) {
// Apple NetworkStatus Compatible Names.
NotReachable = 0,
ReachableViaWiFi = 2,
ReachableViaWWAN = 1
};
@class Reachability;
typedef void (^NetworkReachable)(Reachability * reachability);
typedef void (^NetworkUnreachable)(Reachability * reachability);
@interface Reachability : NSObject
@property (nonatomic, copy) NetworkReachable reachableBlock;
@property (nonatomic, copy) NetworkUnreachable unreachableBlock;
@property (nonatomic, assign) BOOL reachableOnWWAN;
+(Reachability*)reachabilityWithHostname:(NSString*)hostname;
// This is identical to the function above, but is here to maintain
//compatibility with Apples original code. (see .m)
+(Reachability*)reachabilityWithHostName:(NSString*)hostname;
+(Reachability*)reachabilityForInternetConnection;
+(Reachability*)reachabilityWithAddress:(void *)hostAddress;
+(Reachability*)reachabilityForLocalWiFi;
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
-(BOOL)startNotifier;
-(void)stopNotifier;
-(BOOL)isReachable;
-(BOOL)isReachableViaWWAN;
-(BOOL)isReachableViaWiFi;
// WWAN may be available, but not active until a connection has been established.
// WiFi may require a connection for VPN on Demand.
-(BOOL)isConnectionRequired; // Identical DDG variant.
-(BOOL)connectionRequired; // Apple's routine.
// Dynamic, on demand connection?
-(BOOL)isConnectionOnDemand;
// Is user intervention required?
-(BOOL)isInterventionRequired;
-(NetworkStatus)currentReachabilityStatus;
-(SCNetworkReachabilityFlags)reachabilityFlags;
-(NSString*)currentReachabilityString;
-(NSString*)currentReachabilityFlags;
@end
/*
Copyright (c) 2011, Tony Million.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#import "Reachability.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
@interface Reachability ()
@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
@property (nonatomic, strong) id reachabilityObject;
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
@end
static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
{
return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
#if TARGET_OS_IPHONE
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
#else
'X',
#endif
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
}
// Start listening for reachability notifications on the current run loop
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target)
Reachability *reachability = ((__bridge Reachability*)info);
// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
// but what the heck eh?
@autoreleasepool
{
[reachability reachabilityChanged:flags];
}
}
@implementation Reachability
#pragma mark - Class Constructor Methods
+(Reachability*)reachabilityWithHostName:(NSString*)hostname
{
return [Reachability reachabilityWithHostname:hostname];
}
+(Reachability*)reachabilityWithHostname:(NSString*)hostname
{
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
if (ref)
{
id reachability = [[self alloc] initWithReachabilityRef:ref];
return reachability;
}
return nil;
}
+(Reachability *)reachabilityWithAddress:(void *)hostAddress
{
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
if (ref)
{
id reachability = [[self alloc] initWithReachabilityRef:ref];
return reachability;
}
return nil;
}
+(Reachability *)reachabilityForInternetConnection
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return [self reachabilityWithAddress:&zeroAddress];
}
+(Reachability*)reachabilityForLocalWiFi
{
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
localWifiAddress.sin_len = sizeof(localWifiAddress);
localWifiAddress.sin_family = AF_INET;
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
return [self reachabilityWithAddress:&localWifiAddress];
}
// Initialization methods
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
{
self = [super init];
if (self != nil)
{
self.reachableOnWWAN = YES;
self.reachabilityRef = ref;
// We need to create a serial queue.
// We allocate this once for the lifetime of the notifier.
self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
}
return self;
}
-(void)dealloc
{
[self stopNotifier];
if(self.reachabilityRef)
{
CFRelease(self.reachabilityRef);
self.reachabilityRef = nil;
}
self.reachableBlock = nil;
self.unreachableBlock = nil;
self.reachabilitySerialQueue = nil;
}
#pragma mark - Notifier Methods
// Notifier
// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
-(BOOL)startNotifier
{
// allow start notifier to be called multiple times
if(self.reachabilityObject && (self.reachabilityObject == self))
{
return YES;
}
SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
context.info = (__bridge void *)self;
if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
{
// Set it as our reachability queue, which will retain the queue
if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
{
// this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
// woah
self.reachabilityObject = self;
return YES;
}
else
{
#ifdef DEBUG
NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
#endif
// UH OH - FAILURE - stop any callbacks!
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
}
}
else
{
#ifdef DEBUG
NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
#endif
}
// if we get here we fail at the internet
self.reachabilityObject = nil;
return NO;
}
-(void)stopNotifier
{
// First stop, any callbacks!
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
// Unregister target from the GCD serial dispatch queue.
SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
self.reachabilityObject = nil;
}
#pragma mark - reachability tests
// This is for the case where you flick the airplane mode;
// you end up getting something like this:
//Reachability: WR ct-----
//Reachability: -- -------
//Reachability: WR ct-----
//Reachability: -- -------
// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
{
BOOL connectionUP = YES;
if(!(flags & kSCNetworkReachabilityFlagsReachable))
connectionUP = NO;
if( (flags & testcase) == testcase )
connectionUP = NO;
#if TARGET_OS_IPHONE
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
{
// We're on 3G.
if(!self.reachableOnWWAN)
{
// We don't want to connect when on 3G.
connectionUP = NO;
}
}
#endif
return connectionUP;
}
-(BOOL)isReachable
{
SCNetworkReachabilityFlags flags;
if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
return NO;
return [self isReachableWithFlags:flags];
}
-(BOOL)isReachableViaWWAN
{
#if TARGET_OS_IPHONE
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
// Check we're REACHABLE
if(flags & kSCNetworkReachabilityFlagsReachable)
{
// Now, check we're on WWAN
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
{
return YES;
}
}
}
#endif
return NO;
}
-(BOOL)isReachableViaWiFi
{
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
// Check we're reachable
if((flags & kSCNetworkReachabilityFlagsReachable))
{
#if TARGET_OS_IPHONE
// Check we're NOT on WWAN
if((flags & kSCNetworkReachabilityFlagsIsWWAN))
{
return NO;
}
#endif
return YES;
}
}
return NO;
}
// WWAN may be available, but not active until a connection has been established.
// WiFi may require a connection for VPN on Demand.
-(BOOL)isConnectionRequired
{
return [self connectionRequired];
}
-(BOOL)connectionRequired
{
SCNetworkReachabilityFlags flags;
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
// Dynamic, on demand connection?
-(BOOL)isConnectionOnDemand
{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
(flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
}
return NO;
}
// Is user intervention required?
-(BOOL)isInterventionRequired
{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
(flags & kSCNetworkReachabilityFlagsInterventionRequired));
}
return NO;
}
#pragma mark - reachability status stuff
-(NetworkStatus)currentReachabilityStatus
{
if([self isReachable])
{
if([self isReachableViaWiFi])
return ReachableViaWiFi;
#if TARGET_OS_IPHONE
return ReachableViaWWAN;
#endif
}
return NotReachable;
}
-(SCNetworkReachabilityFlags)reachabilityFlags
{
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
return flags;
}
return 0;
}
-(NSString*)currentReachabilityString
{
NetworkStatus temp = [self currentReachabilityStatus];
if(temp == ReachableViaWWAN)
{
// Updated for the fact that we have CDMA phones now!
return NSLocalizedString(@"Cellular", @"");
}
if (temp == ReachableViaWiFi)
{
return NSLocalizedString(@"WiFi", @"");
}
return NSLocalizedString(@"No Connection", @"");
}
-(NSString*)currentReachabilityFlags
{
return reachabilityFlags([self reachabilityFlags]);
}
#pragma mark - Callback function calls this method
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
{
if([self isReachableWithFlags:flags])
{
if(self.reachableBlock)
{
self.reachableBlock(self);
}
}
else
{
if(self.unreachableBlock)
{
self.unreachableBlock(self);
}
}
// this makes sure the change notification happens on the MAIN THREAD
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
object:self];
});
}
#pragma mark - Debug Description
- (NSString *) description
{
NSString *description = [NSString stringWithFormat:@"<%@: %#x (%@)>",
NSStringFromClass([self class]), (unsigned int) self, [self currentReachabilityFlags]];
return description;
}
@end
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Bugly"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/CocoaLumberjack" "${PODS_ROOT}/Headers/Public/GInsightSDK" "${PODS_ROOT}/Headers/Public/JAnalytics" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/ZJAnimationPopView"
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/ZJAnimationPopView" "${PODS_ROOT}/GInsightSDK" "${PODS_ROOT}/JAnalytics" "${PODS_ROOT}/JCore"
OTHER_LDFLAGS = $(inherited) -ObjC -l"CocoaLumberjack" -l"GInsightSDK" -l"GTCommonSDK" -l"Masonry" -l"SVProgressHUD" -l"ZJAnimationPopView" -l"c++" -l"janalytics-ios-2.0.0" -l"jcore-ios-2.0.2" -l"resolv" -l"sqlite3.0" -l"z" -framework "AdSupport" -framework "Bugly" -framework "CFNetwork" -framework "CoreBluetooth" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -weak_framework "UserNotifications"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/CocoaLumberjack" "${PODS_ROOT}/Headers/Public/GInsightSDK" "${PODS_ROOT}/Headers/Public/JAnalytics" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/Reachability" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/Toast" "${PODS_ROOT}/Headers/Public/UICountingLabel" "${PODS_ROOT}/Headers/Public/ZJAnimationPopView"
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/Toast" "${PODS_CONFIGURATION_BUILD_DIR}/UICountingLabel" "${PODS_CONFIGURATION_BUILD_DIR}/ZJAnimationPopView" "${PODS_ROOT}/GInsightSDK" "${PODS_ROOT}/JAnalytics" "${PODS_ROOT}/JCore"
OTHER_LDFLAGS = $(inherited) -ObjC -l"CocoaLumberjack" -l"GInsightSDK" -l"GTCommonSDK" -l"Masonry" -l"Reachability" -l"SVProgressHUD" -l"Toast" -l"UICountingLabel" -l"ZJAnimationPopView" -l"c++" -l"janalytics-ios-2.0.0" -l"jcore-ios-2.0.2" -l"resolv" -l"sqlite3.0" -l"z" -framework "AdSupport" -framework "Bugly" -framework "CFNetwork" -framework "CoreBluetooth" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -weak_framework "UserNotifications"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Bugly"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/CocoaLumberjack" "${PODS_ROOT}/Headers/Public/GInsightSDK" "${PODS_ROOT}/Headers/Public/JAnalytics" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/ZJAnimationPopView"
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/ZJAnimationPopView" "${PODS_ROOT}/GInsightSDK" "${PODS_ROOT}/JAnalytics" "${PODS_ROOT}/JCore"
OTHER_LDFLAGS = $(inherited) -ObjC -l"CocoaLumberjack" -l"GInsightSDK" -l"GTCommonSDK" -l"Masonry" -l"SVProgressHUD" -l"ZJAnimationPopView" -l"c++" -l"janalytics-ios-2.0.0" -l"jcore-ios-2.0.2" -l"resolv" -l"sqlite3.0" -l"z" -framework "AdSupport" -framework "Bugly" -framework "CFNetwork" -framework "CoreBluetooth" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -weak_framework "UserNotifications"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/CocoaLumberjack" "${PODS_ROOT}/Headers/Public/GInsightSDK" "${PODS_ROOT}/Headers/Public/JAnalytics" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/Reachability" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/Toast" "${PODS_ROOT}/Headers/Public/UICountingLabel" "${PODS_ROOT}/Headers/Public/ZJAnimationPopView"
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/Toast" "${PODS_CONFIGURATION_BUILD_DIR}/UICountingLabel" "${PODS_CONFIGURATION_BUILD_DIR}/ZJAnimationPopView" "${PODS_ROOT}/GInsightSDK" "${PODS_ROOT}/JAnalytics" "${PODS_ROOT}/JCore"
OTHER_LDFLAGS = $(inherited) -ObjC -l"CocoaLumberjack" -l"GInsightSDK" -l"GTCommonSDK" -l"Masonry" -l"Reachability" -l"SVProgressHUD" -l"Toast" -l"UICountingLabel" -l"ZJAnimationPopView" -l"c++" -l"janalytics-ios-2.0.0" -l"jcore-ios-2.0.2" -l"resolv" -l"sqlite3.0" -l"z" -framework "AdSupport" -framework "Bugly" -framework "CFNetwork" -framework "CoreBluetooth" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -weak_framework "UserNotifications"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
#import <Foundation/Foundation.h>
@interface PodsDummy_Reachability : NSObject
@end
@implementation PodsDummy_Reachability
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Reachability
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Reachability" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Reachability"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Reachability
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
#import <Foundation/Foundation.h>
@interface PodsDummy_Toast : NSObject
@end
@implementation PodsDummy_Toast
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Toast
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Toast" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Toast"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Toast
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
#import <Foundation/Foundation.h>
@interface PodsDummy_UICountingLabel : NSObject
@end
@implementation PodsDummy_UICountingLabel
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/UICountingLabel
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/UICountingLabel" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/UICountingLabel"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/UICountingLabel
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
Toast for iOS
=============
[![Build Status](https://travis-ci.org/scalessec/Toast.svg?branch=3.0)](https://travis-ci.org/scalessec/Toast)
[![CocoaPods Version](https://img.shields.io/cocoapods/v/Toast.svg)](http://cocoadocs.org/docsets/Toast)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
Toast is an Objective-C category that adds toast notifications to the `UIView` object class. It is intended to be simple, lightweight, and easy to use. Most
toast notifications can be triggered with a single line of code.
**Using Swift? A native swift port of this library is now available: [Toast-Swift](https://github.com/scalessec/Toast-Swift "Toast-Swift")**
Screenshots
---------
![Toast Screenshots](toast_screenshots.jpg)
Basic Examples
---------
```objc
// basic usage
[self.view makeToast:@"This is a piece of toast."];
// toast with a specific duration and position
[self.view makeToast:@"This is a piece of toast with a specific duration and position."
duration:3.0
position:CSToastPositionTop];
// toast with all possible options
[self.view makeToast:@"This is a piece of toast with a title & image"
duration:3.0
position:[NSValue valueWithCGPoint:CGPointMake(110, 110)]
title:@"Toast Title"
image:[UIImage imageNamed:@"toast.png"]
style:nil
completion:^(BOOL didTap) {
if (didTap) {
NSLog(@"completion from tap");
} else {
NSLog(@"completion without tap");
}
}];
// display toast with an activity spinner
[self.view makeToastActivity:CSToastPositionCenter];
// display any view as toast
[self.view showToast:myView];
```
But wait, there's more!
---------
```objc
// create a new style
CSToastStyle *style = [[CSToastStyle alloc] initWithDefaultStyle];
// this is just one of many style options
style.messageColor = [UIColor orangeColor];
// present the toast with the new style
[self.view makeToast:@"This is a piece of toast."
duration:3.0
position:CSToastPositionBottom
style:style];
// or perhaps you want to use this style for all toasts going forward?
// just set the shared style and there's no need to provide the style again
[CSToastManager setSharedStyle:style];
// toggle "tap to dismiss" functionality
[CSToastManager setTapToDismissEnabled:YES];
// toggle queueing behavior
[CSToastManager setQueueEnabled:YES];
// immediately hides all toast views in self.view
[self.view hideAllToasts];
```
See the demo project for more examples.
Setup Instructions
------------------
[CocoaPods](http://cocoapods.org)
------------------
Install with CocoaPods by adding the following to your `Podfile`:
```ruby
pod 'Toast', '~> 4.0.0'
```
[Carthage](https://github.com/Carthage/Carthage)
------------------
Install with Carthage by adding the following to your `Cartfile`:
```ogdl
github "scalessec/Toast" ~> 4.0.0
```
Run `carthage update` to build the framework and link against `Toast.framework`. Then, `#import <Toast/Toast.h>`.
Manually
--------
1. Add `UIView+Toast.h` & `UIView+Toast.m` to your project.
2. `#import "UIView+Toast.h"`
3. Grab yourself a cold 🍺.
MIT License
-----------
Copyright (c) 2011-2017 Charles Scalesse.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Toast.h
// Toast
//
// Created by Charles Scalesse on 11/3/16.
//
//
#import <UIKit/UIKit.h>
//! Project version number for Toast.
FOUNDATION_EXPORT double ToastVersionNumber;
//! Project version string for Toast.
FOUNDATION_EXPORT const unsigned char ToastVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Toast/PublicHeader.h>
#import <Toast/UIView+Toast.h>
//
// UIView+Toast.h
// Toast
//
// Copyright (c) 2011-2017 Charles Scalesse.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <UIKit/UIKit.h>
extern const NSString * CSToastPositionTop;
extern const NSString * CSToastPositionCenter;
extern const NSString * CSToastPositionBottom;
@class CSToastStyle;
/**
Toast is an Objective-C category that adds toast notifications to the UIView
object class. It is intended to be simple, lightweight, and easy to use. Most
toast notifications can be triggered with a single line of code.
The `makeToast:` methods create a new view and then display it as toast.
The `showToast:` methods display any view as toast.
*/
@interface UIView (Toast)
/**
Creates and presents a new toast view with a message and displays it with the
default duration and position. Styled using the shared style.
@param message The message to be displayed
*/
- (void)makeToast:(NSString *)message;
/**
Creates and presents a new toast view with a message. Duration and position
can be set explicitly. Styled using the shared style.
@param message The message to be displayed
@param duration The toast duration
@param position The toast's center point. Can be one of the predefined CSToastPosition
constants or a `CGPoint` wrapped in an `NSValue` object.
*/
- (void)makeToast:(NSString *)message
duration:(NSTimeInterval)duration
position:(id)position;
/**
Creates and presents a new toast view with a message. Duration, position, and
style can be set explicitly.
@param message The message to be displayed
@param duration The toast duration
@param position The toast's center point. Can be one of the predefined CSToastPosition
constants or a `CGPoint` wrapped in an `NSValue` object.
@param style The style. The shared style will be used when nil
*/
- (void)makeToast:(NSString *)message
duration:(NSTimeInterval)duration
position:(id)position
style:(CSToastStyle *)style;
/**
Creates and presents a new toast view with a message, title, and image. Duration,
position, and style can be set explicitly. The completion block executes when the
toast view completes. `didTap` will be `YES` if the toast view was dismissed from
a tap.
@param message The message to be displayed
@param duration The toast duration
@param position The toast's center point. Can be one of the predefined CSToastPosition
constants or a `CGPoint` wrapped in an `NSValue` object.
@param title The title
@param image The image
@param style The style. The shared style will be used when nil
@param completion The completion block, executed after the toast view disappears.
didTap will be `YES` if the toast view was dismissed from a tap.
*/
- (void)makeToast:(NSString *)message
duration:(NSTimeInterval)duration
position:(id)position
title:(NSString *)title
image:(UIImage *)image
style:(CSToastStyle *)style
completion:(void(^)(BOOL didTap))completion;
/**
Creates a new toast view with any combination of message, title, and image.
The look and feel is configured via the style. Unlike the `makeToast:` methods,
this method does not present the toast view automatically. One of the showToast:
methods must be used to present the resulting view.
@warning if message, title, and image are all nil, this method will return nil.
@param message The message to be displayed
@param title The title
@param image The image
@param style The style. The shared style will be used when nil
@return The newly created toast view
*/
- (UIView *)toastViewForMessage:(NSString *)message
title:(NSString *)title
image:(UIImage *)image
style:(CSToastStyle *)style;
/**
Hides the active toast. If there are multiple toasts active in a view, this method
hides the oldest toast (the first of the toasts to have been presented).
@see `hideAllToasts` to remove all active toasts from a view.
@warning This method has no effect on activity toasts. Use `hideToastActivity` to
hide activity toasts.
*/
- (void)hideToast;
/**
Hides an active toast.
@param toast The active toast view to dismiss. Any toast that is currently being displayed
on the screen is considered active.
@warning this does not clear a toast view that is currently waiting in the queue.
*/
- (void)hideToast:(UIView *)toast;
/**
Hides all active toast views and clears the queue.
*/
- (void)hideAllToasts;
/**
Hides all active toast views, with options to hide activity and clear the queue.
@param includeActivity If `true`, toast activity will also be hidden. Default is `false`.
@param clearQueue If `true`, removes all toast views from the queue. Default is `true`.
*/
- (void)hideAllToasts:(BOOL)includeActivity clearQueue:(BOOL)clearQueue;
/**
Removes all toast views from the queue. This has no effect on toast views that are
active. Use `hideAllToasts` to hide the active toasts views and clear the queue.
*/
- (void)clearToastQueue;
/**
Creates and displays a new toast activity indicator view at a specified position.
@warning Only one toast activity indicator view can be presented per superview. Subsequent
calls to `makeToastActivity:` will be ignored until hideToastActivity is called.
@warning `makeToastActivity:` works independently of the showToast: methods. Toast activity
views can be presented and dismissed while toast views are being displayed. `makeToastActivity:`
has no effect on the queueing behavior of the showToast: methods.
@param position The toast's center point. Can be one of the predefined CSToastPosition
constants or a `CGPoint` wrapped in an `NSValue` object.
*/
- (void)makeToastActivity:(id)position;
/**
Dismisses the active toast activity indicator view.
*/
- (void)hideToastActivity;
/**
Displays any view as toast using the default duration and position.
@param toast The view to be displayed as toast
*/
- (void)showToast:(UIView *)toast;
/**
Displays any view as toast at a provided position and duration. The completion block
executes when the toast view completes. `didTap` will be `YES` if the toast view was
dismissed from a tap.
@param toast The view to be displayed as toast
@param duration The notification duration
@param position The toast's center point. Can be one of the predefined CSToastPosition
constants or a `CGPoint` wrapped in an `NSValue` object.
@param completion The completion block, executed after the toast view disappears.
didTap will be `YES` if the toast view was dismissed from a tap.
*/
- (void)showToast:(UIView *)toast
duration:(NSTimeInterval)duration
position:(id)position
completion:(void(^)(BOOL didTap))completion;
@end
/**
`CSToastStyle` instances define the look and feel for toast views created via the
`makeToast:` methods as well for toast views created directly with
`toastViewForMessage:title:image:style:`.
@warning `CSToastStyle` offers relatively simple styling options for the default
toast view. If you require a toast view with more complex UI, it probably makes more
sense to create your own custom UIView subclass and present it with the `showToast:`
methods.
*/
@interface CSToastStyle : NSObject
/**
The background color. Default is `[UIColor blackColor]` at 80% opacity.
*/
@property (strong, nonatomic) UIColor *backgroundColor;
/**
The title color. Default is `[UIColor whiteColor]`.
*/
@property (strong, nonatomic) UIColor *titleColor;
/**
The message color. Default is `[UIColor whiteColor]`.
*/
@property (strong, nonatomic) UIColor *messageColor;
/**
A percentage value from 0.0 to 1.0, representing the maximum width of the toast
view relative to it's superview. Default is 0.8 (80% of the superview's width).
*/
@property (assign, nonatomic) CGFloat maxWidthPercentage;
/**
A percentage value from 0.0 to 1.0, representing the maximum height of the toast
view relative to it's superview. Default is 0.8 (80% of the superview's height).
*/
@property (assign, nonatomic) CGFloat maxHeightPercentage;
/**
The spacing from the horizontal edge of the toast view to the content. When an image
is present, this is also used as the padding between the image and the text.
Default is 10.0.
*/
@property (assign, nonatomic) CGFloat horizontalPadding;
/**
The spacing from the vertical edge of the toast view to the content. When a title
is present, this is also used as the padding between the title and the message.
Default is 10.0.
*/
@property (assign, nonatomic) CGFloat verticalPadding;
/**
The corner radius. Default is 10.0.
*/
@property (assign, nonatomic) CGFloat cornerRadius;
/**
The title font. Default is `[UIFont boldSystemFontOfSize:16.0]`.
*/
@property (strong, nonatomic) UIFont *titleFont;
/**
The message font. Default is `[UIFont systemFontOfSize:16.0]`.
*/
@property (strong, nonatomic) UIFont *messageFont;
/**
The title text alignment. Default is `NSTextAlignmentLeft`.
*/
@property (assign, nonatomic) NSTextAlignment titleAlignment;
/**
The message text alignment. Default is `NSTextAlignmentLeft`.
*/
@property (assign, nonatomic) NSTextAlignment messageAlignment;
/**
The maximum number of lines for the title. The default is 0 (no limit).
*/
@property (assign, nonatomic) NSInteger titleNumberOfLines;
/**
The maximum number of lines for the message. The default is 0 (no limit).
*/
@property (assign, nonatomic) NSInteger messageNumberOfLines;
/**
Enable or disable a shadow on the toast view. Default is `NO`.
*/
@property (assign, nonatomic) BOOL displayShadow;
/**
The shadow color. Default is `[UIColor blackColor]`.
*/
@property (strong, nonatomic) UIColor *shadowColor;
/**
A value from 0.0 to 1.0, representing the opacity of the shadow.
Default is 0.8 (80% opacity).
*/
@property (assign, nonatomic) CGFloat shadowOpacity;
/**
The shadow radius. Default is 6.0.
*/
@property (assign, nonatomic) CGFloat shadowRadius;
/**
The shadow offset. The default is `CGSizeMake(4.0, 4.0)`.
*/
@property (assign, nonatomic) CGSize shadowOffset;
/**
The image size. The default is `CGSizeMake(80.0, 80.0)`.
*/
@property (assign, nonatomic) CGSize imageSize;
/**
The size of the toast activity view when `makeToastActivity:` is called.
Default is `CGSizeMake(100.0, 100.0)`.
*/
@property (assign, nonatomic) CGSize activitySize;
/**
The fade in/out animation duration. Default is 0.2.
*/
@property (assign, nonatomic) NSTimeInterval fadeDuration;
/**
Creates a new instance of `CSToastStyle` with all the default values set.
*/
- (instancetype)initWithDefaultStyle NS_DESIGNATED_INITIALIZER;
/**
@warning Only the designated initializer should be used to create
an instance of `CSToastStyle`.
*/
- (instancetype)init NS_UNAVAILABLE;
@end
/**
`CSToastManager` provides general configuration options for all toast
notifications. Backed by a singleton instance.
*/
@interface CSToastManager : NSObject
/**
Sets the shared style on the singleton. The shared style is used whenever
a `makeToast:` method (or `toastViewForMessage:title:image:style:`) is called
with with a nil style. By default, this is set to `CSToastStyle`'s default
style.
@param sharedStyle the shared style
*/
+ (void)setSharedStyle:(CSToastStyle *)sharedStyle;
/**
Gets the shared style from the singlton. By default, this is
`CSToastStyle`'s default style.
@return the shared style
*/
+ (CSToastStyle *)sharedStyle;
/**
Enables or disables tap to dismiss on toast views. Default is `YES`.
@param tapToDismissEnabled YES or NO
*/
+ (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled;
/**
Returns `YES` if tap to dismiss is enabled, otherwise `NO`.
Default is `YES`.
@return BOOL YES or NO
*/
+ (BOOL)isTapToDismissEnabled;
/**
Enables or disables queueing behavior for toast views. When `YES`,
toast views will appear one after the other. When `NO`, multiple Toast
views will appear at the same time (potentially overlapping depending
on their positions). This has no effect on the toast activity view,
which operates independently of normal toast views. Default is `NO`.
@param queueEnabled YES or NO
*/
+ (void)setQueueEnabled:(BOOL)queueEnabled;
/**
Returns `YES` if the queue is enabled, otherwise `NO`.
Default is `NO`.
@return BOOL
*/
+ (BOOL)isQueueEnabled;
/**
Sets the default duration. Used for the `makeToast:` and
`showToast:` methods that don't require an explicit duration.
Default is 3.0.
@param duration The toast duration
*/
+ (void)setDefaultDuration:(NSTimeInterval)duration;
/**
Returns the default duration. Default is 3.0.
@return duration The toast duration
*/
+ (NSTimeInterval)defaultDuration;
/**
Sets the default position. Used for the `makeToast:` and
`showToast:` methods that don't require an explicit position.
Default is `CSToastPositionBottom`.
@param position The default center point. Can be one of the predefined
CSToastPosition constants or a `CGPoint` wrapped in an `NSValue` object.
*/
+ (void)setDefaultPosition:(id)position;
/**
Returns the default toast position. Default is `CSToastPositionBottom`.
@return position The default center point. Will be one of the predefined
CSToastPosition constants or a `CGPoint` wrapped in an `NSValue` object.
*/
+ (id)defaultPosition;
@end
//
// UIView+Toast.m
// Toast
//
// Copyright (c) 2011-2017 Charles Scalesse.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "UIView+Toast.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
// Positions
NSString * CSToastPositionTop = @"CSToastPositionTop";
NSString * CSToastPositionCenter = @"CSToastPositionCenter";
NSString * CSToastPositionBottom = @"CSToastPositionBottom";
// Keys for values associated with toast views
static const NSString * CSToastTimerKey = @"CSToastTimerKey";
static const NSString * CSToastDurationKey = @"CSToastDurationKey";
static const NSString * CSToastPositionKey = @"CSToastPositionKey";
static const NSString * CSToastCompletionKey = @"CSToastCompletionKey";
// Keys for values associated with self
static const NSString * CSToastActiveKey = @"CSToastActiveKey";
static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey";
static const NSString * CSToastQueueKey = @"CSToastQueueKey";
@interface UIView (ToastPrivate)
/**
These private methods are being prefixed with "cs_" to reduce the likelihood of non-obvious
naming conflicts with other UIView methods.
@discussion Should the public API also use the cs_ prefix? Technically it should, but it
results in code that is less legible. The current public method names seem unlikely to cause
conflicts so I think we should favor the cleaner API for now.
*/
- (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position;
- (void)cs_hideToast:(UIView *)toast;
- (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap;
- (void)cs_toastTimerDidFinish:(NSTimer *)timer;
- (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer;
- (CGPoint)cs_centerPointForPosition:(id)position withToast:(UIView *)toast;
- (NSMutableArray *)cs_toastQueue;
@end
@implementation UIView (Toast)
#pragma mark - Make Toast Methods
- (void)makeToast:(NSString *)message {
[self makeToast:message duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] style:nil];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position {
[self makeToast:message duration:duration position:position style:nil];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position style:(CSToastStyle *)style {
UIView *toast = [self toastViewForMessage:message title:nil image:nil style:style];
[self showToast:toast duration:duration position:position completion:nil];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style completion:(void(^)(BOOL didTap))completion {
UIView *toast = [self toastViewForMessage:message title:title image:image style:style];
[self showToast:toast duration:duration position:position completion:completion];
}
#pragma mark - Show Toast Methods
- (void)showToast:(UIView *)toast {
[self showToast:toast duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] completion:nil];
}
- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position completion:(void(^)(BOOL didTap))completion {
// sanity
if (toast == nil) return;
// store the completion block on the toast view
objc_setAssociatedObject(toast, &CSToastCompletionKey, completion, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([CSToastManager isQueueEnabled] && [self.cs_activeToasts count] > 0) {
// we're about to queue this toast view so we need to store the duration and position as well
objc_setAssociatedObject(toast, &CSToastDurationKey, @(duration), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(toast, &CSToastPositionKey, position, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// enqueue
[self.cs_toastQueue addObject:toast];
} else {
// present
[self cs_showToast:toast duration:duration position:position];
}
}
#pragma mark - Hide Toast Methods
- (void)hideToast {
[self hideToast:[[self cs_activeToasts] firstObject]];
}
- (void)hideToast:(UIView *)toast {
// sanity
if (!toast || ![[self cs_activeToasts] containsObject:toast]) return;
[self cs_hideToast:toast];
}
- (void)hideAllToasts {
[self hideAllToasts:NO clearQueue:YES];
}
- (void)hideAllToasts:(BOOL)includeActivity clearQueue:(BOOL)clearQueue {
if (clearQueue) {
[self clearToastQueue];
}
for (UIView *toast in [self cs_activeToasts]) {
[self hideToast:toast];
}
if (includeActivity) {
[self hideToastActivity];
}
}
- (void)clearToastQueue {
[[self cs_toastQueue] removeAllObjects];
}
#pragma mark - Private Show/Hide Methods
- (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position {
toast.center = [self cs_centerPointForPosition:position withToast:toast];
toast.alpha = 0.0;
if ([CSToastManager isTapToDismissEnabled]) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cs_handleToastTapped:)];
[toast addGestureRecognizer:recognizer];
toast.userInteractionEnabled = YES;
toast.exclusiveTouch = YES;
}
[[self cs_activeToasts] addObject:toast];
[self addSubview:toast];
[UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration]
delay:0.0
options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction)
animations:^{
toast.alpha = 1.0;
} completion:^(BOOL finished) {
NSTimer *timer = [NSTimer timerWithTimeInterval:duration target:self selector:@selector(cs_toastTimerDidFinish:) userInfo:toast repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
objc_setAssociatedObject(toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}];
}
- (void)cs_hideToast:(UIView *)toast {
[self cs_hideToast:toast fromTap:NO];
}
- (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap {
NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey);
[timer invalidate];
[UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration]
delay:0.0
options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
animations:^{
toast.alpha = 0.0;
} completion:^(BOOL finished) {
[toast removeFromSuperview];
// remove
[[self cs_activeToasts] removeObject:toast];
// execute the completion block, if necessary
void (^completion)(BOOL didTap) = objc_getAssociatedObject(toast, &CSToastCompletionKey);
if (completion) {
completion(fromTap);
}
if ([self.cs_toastQueue count] > 0) {
// dequeue
UIView *nextToast = [[self cs_toastQueue] firstObject];
[[self cs_toastQueue] removeObjectAtIndex:0];
// present the next toast
NSTimeInterval duration = [objc_getAssociatedObject(nextToast, &CSToastDurationKey) doubleValue];
id position = objc_getAssociatedObject(nextToast, &CSToastPositionKey);
[self cs_showToast:nextToast duration:duration position:position];
}
}];
}
#pragma mark - View Construction
- (UIView *)toastViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style {
// sanity
if (message == nil && title == nil && image == nil) return nil;
// default to the shared style
if (style == nil) {
style = [CSToastManager sharedStyle];
}
// dynamically build a toast view with any combination of message, title, & image
UILabel *messageLabel = nil;
UILabel *titleLabel = nil;
UIImageView *imageView = nil;
UIView *wrapperView = [[UIView alloc] init];
wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
wrapperView.layer.cornerRadius = style.cornerRadius;
if (style.displayShadow) {
wrapperView.layer.shadowColor = style.shadowColor.CGColor;
wrapperView.layer.shadowOpacity = style.shadowOpacity;
wrapperView.layer.shadowRadius = style.shadowRadius;
wrapperView.layer.shadowOffset = style.shadowOffset;
}
wrapperView.backgroundColor = style.backgroundColor;
if(image != nil) {
imageView = [[UIImageView alloc] initWithImage:image];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.frame = CGRectMake(style.horizontalPadding, style.verticalPadding, style.imageSize.width, style.imageSize.height);
}
CGRect imageRect = CGRectZero;
if(imageView != nil) {
imageRect.origin.x = style.horizontalPadding;
imageRect.origin.y = style.verticalPadding;
imageRect.size.width = imageView.bounds.size.width;
imageRect.size.height = imageView.bounds.size.height;
}
if (title != nil) {
titleLabel = [[UILabel alloc] init];
titleLabel.numberOfLines = style.titleNumberOfLines;
titleLabel.font = style.titleFont;
titleLabel.textAlignment = style.titleAlignment;
titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
titleLabel.textColor = style.titleColor;
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.alpha = 1.0;
titleLabel.text = title;
// size the title label according to the length of the text
CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage);
CGSize expectedSizeTitle = [titleLabel sizeThatFits:maxSizeTitle];
// UILabel can return a size larger than the max size when the number of lines is 1
expectedSizeTitle = CGSizeMake(MIN(maxSizeTitle.width, expectedSizeTitle.width), MIN(maxSizeTitle.height, expectedSizeTitle.height));
titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height);
}
if (message != nil) {
messageLabel = [[UILabel alloc] init];
messageLabel.numberOfLines = style.messageNumberOfLines;
messageLabel.font = style.messageFont;
messageLabel.textAlignment = style.messageAlignment;
messageLabel.lineBreakMode = NSLineBreakByTruncatingTail;
messageLabel.textColor = style.messageColor;
messageLabel.backgroundColor = [UIColor clearColor];
messageLabel.alpha = 1.0;
messageLabel.text = message;
CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage);
CGSize expectedSizeMessage = [messageLabel sizeThatFits:maxSizeMessage];
// UILabel can return a size larger than the max size when the number of lines is 1
expectedSizeMessage = CGSizeMake(MIN(maxSizeMessage.width, expectedSizeMessage.width), MIN(maxSizeMessage.height, expectedSizeMessage.height));
messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height);
}
CGRect titleRect = CGRectZero;
if(titleLabel != nil) {
titleRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding;
titleRect.origin.y = style.verticalPadding;
titleRect.size.width = titleLabel.bounds.size.width;
titleRect.size.height = titleLabel.bounds.size.height;
}
CGRect messageRect = CGRectZero;
if(messageLabel != nil) {
messageRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding;
messageRect.origin.y = titleRect.origin.y + titleRect.size.height + style.verticalPadding;
messageRect.size.width = messageLabel.bounds.size.width;
messageRect.size.height = messageLabel.bounds.size.height;
}
CGFloat longerWidth = MAX(titleRect.size.width, messageRect.size.width);
CGFloat longerX = MAX(titleRect.origin.x, messageRect.origin.x);
// Wrapper width uses the longerWidth or the image width, whatever is larger. Same logic applies to the wrapper height.
CGFloat wrapperWidth = MAX((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding));
CGFloat wrapperHeight = MAX((messageRect.origin.y + messageRect.size.height + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0)));
wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight);
if(titleLabel != nil) {
titleLabel.frame = titleRect;
[wrapperView addSubview:titleLabel];
}
if(messageLabel != nil) {
messageLabel.frame = messageRect;
[wrapperView addSubview:messageLabel];
}
if(imageView != nil) {
[wrapperView addSubview:imageView];
}
return wrapperView;
}
#pragma mark - Storage
- (NSMutableArray *)cs_activeToasts {
NSMutableArray *cs_activeToasts = objc_getAssociatedObject(self, &CSToastActiveKey);
if (cs_activeToasts == nil) {
cs_activeToasts = [[NSMutableArray alloc] init];
objc_setAssociatedObject(self, &CSToastActiveKey, cs_activeToasts, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return cs_activeToasts;
}
- (NSMutableArray *)cs_toastQueue {
NSMutableArray *cs_toastQueue = objc_getAssociatedObject(self, &CSToastQueueKey);
if (cs_toastQueue == nil) {
cs_toastQueue = [[NSMutableArray alloc] init];
objc_setAssociatedObject(self, &CSToastQueueKey, cs_toastQueue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return cs_toastQueue;
}
#pragma mark - Events
- (void)cs_toastTimerDidFinish:(NSTimer *)timer {
[self cs_hideToast:(UIView *)timer.userInfo];
}
- (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer {
UIView *toast = recognizer.view;
NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey);
[timer invalidate];
[self cs_hideToast:toast fromTap:YES];
}
#pragma mark - Activity Methods
- (void)makeToastActivity:(id)position {
// sanity
UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
if (existingActivityView != nil) return;
CSToastStyle *style = [CSToastManager sharedStyle];
UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, style.activitySize.width, style.activitySize.height)];
activityView.center = [self cs_centerPointForPosition:position withToast:activityView];
activityView.backgroundColor = style.backgroundColor;
activityView.alpha = 0.0;
activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
activityView.layer.cornerRadius = style.cornerRadius;
if (style.displayShadow) {
activityView.layer.shadowColor = style.shadowColor.CGColor;
activityView.layer.shadowOpacity = style.shadowOpacity;
activityView.layer.shadowRadius = style.shadowRadius;
activityView.layer.shadowOffset = style.shadowOffset;
}
UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2);
[activityView addSubview:activityIndicatorView];
[activityIndicatorView startAnimating];
// associate the activity view with self
objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:activityView];
[UIView animateWithDuration:style.fadeDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
activityView.alpha = 1.0;
} completion:nil];
}
- (void)hideToastActivity {
UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
if (existingActivityView != nil) {
[UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration]
delay:0.0
options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
animations:^{
existingActivityView.alpha = 0.0;
} completion:^(BOOL finished) {
[existingActivityView removeFromSuperview];
objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}];
}
}
#pragma mark - Helpers
- (CGPoint)cs_centerPointForPosition:(id)point withToast:(UIView *)toast {
CSToastStyle *style = [CSToastManager sharedStyle];
UIEdgeInsets safeInsets = UIEdgeInsetsZero;
if (@available(iOS 11.0, *)) {
safeInsets = self.safeAreaInsets;
}
CGFloat topPadding = style.verticalPadding + safeInsets.top;
CGFloat bottomPadding = style.verticalPadding + safeInsets.bottom;
if([point isKindOfClass:[NSString class]]) {
if([point caseInsensitiveCompare:CSToastPositionTop] == NSOrderedSame) {
return CGPointMake(self.bounds.size.width / 2.0, (toast.frame.size.height / 2.0) + topPadding);
} else if([point caseInsensitiveCompare:CSToastPositionCenter] == NSOrderedSame) {
return CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0);
}
} else if ([point isKindOfClass:[NSValue class]]) {
return [point CGPointValue];
}
// default to bottom
return CGPointMake(self.bounds.size.width / 2.0, (self.bounds.size.height - (toast.frame.size.height / 2.0)) - bottomPadding);
}
@end
@implementation CSToastStyle
#pragma mark - Constructors
- (instancetype)initWithDefaultStyle {
self = [super init];
if (self) {
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
self.titleColor = [UIColor whiteColor];
self.messageColor = [UIColor whiteColor];
self.maxWidthPercentage = 0.8;
self.maxHeightPercentage = 0.8;
self.horizontalPadding = 10.0;
self.verticalPadding = 10.0;
self.cornerRadius = 10.0;
self.titleFont = [UIFont boldSystemFontOfSize:16.0];
self.messageFont = [UIFont systemFontOfSize:16.0];
self.titleAlignment = NSTextAlignmentLeft;
self.messageAlignment = NSTextAlignmentLeft;
self.titleNumberOfLines = 0;
self.messageNumberOfLines = 0;
self.displayShadow = NO;
self.shadowOpacity = 0.8;
self.shadowRadius = 6.0;
self.shadowOffset = CGSizeMake(4.0, 4.0);
self.imageSize = CGSizeMake(80.0, 80.0);
self.activitySize = CGSizeMake(100.0, 100.0);
self.fadeDuration = 0.2;
}
return self;
}
- (void)setMaxWidthPercentage:(CGFloat)maxWidthPercentage {
_maxWidthPercentage = MAX(MIN(maxWidthPercentage, 1.0), 0.0);
}
- (void)setMaxHeightPercentage:(CGFloat)maxHeightPercentage {
_maxHeightPercentage = MAX(MIN(maxHeightPercentage, 1.0), 0.0);
}
- (instancetype)init NS_UNAVAILABLE {
return nil;
}
@end
@interface CSToastManager ()
@property (strong, nonatomic) CSToastStyle *sharedStyle;
@property (assign, nonatomic, getter=isTapToDismissEnabled) BOOL tapToDismissEnabled;
@property (assign, nonatomic, getter=isQueueEnabled) BOOL queueEnabled;
@property (assign, nonatomic) NSTimeInterval defaultDuration;
@property (strong, nonatomic) id defaultPosition;
@end
@implementation CSToastManager
#pragma mark - Constructors
+ (instancetype)sharedManager {
static CSToastManager *_sharedManager = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
- (instancetype)init {
self = [super init];
if (self) {
self.sharedStyle = [[CSToastStyle alloc] initWithDefaultStyle];
self.tapToDismissEnabled = YES;
self.queueEnabled = NO;
self.defaultDuration = 3.0;
self.defaultPosition = CSToastPositionBottom;
}
return self;
}
#pragma mark - Singleton Methods
+ (void)setSharedStyle:(CSToastStyle *)sharedStyle {
[[self sharedManager] setSharedStyle:sharedStyle];
}
+ (CSToastStyle *)sharedStyle {
return [[self sharedManager] sharedStyle];
}
+ (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled {
[[self sharedManager] setTapToDismissEnabled:tapToDismissEnabled];
}
+ (BOOL)isTapToDismissEnabled {
return [[self sharedManager] isTapToDismissEnabled];
}
+ (void)setQueueEnabled:(BOOL)queueEnabled {
[[self sharedManager] setQueueEnabled:queueEnabled];
}
+ (BOOL)isQueueEnabled {
return [[self sharedManager] isQueueEnabled];
}
+ (void)setDefaultDuration:(NSTimeInterval)duration {
[[self sharedManager] setDefaultDuration:duration];
}
+ (NSTimeInterval)defaultDuration {
return [[self sharedManager] defaultDuration];
}
+ (void)setDefaultPosition:(id)position {
if ([position isKindOfClass:[NSString class]] || [position isKindOfClass:[NSValue class]]) {
[[self sharedManager] setDefaultPosition:position];
}
}
+ (id)defaultPosition {
return [[self sharedManager] defaultPosition];
}
@end
Copyright (c) 2011-2017 Charles Scalesse.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The MIT License (MIT)
Copyright (c) 2013 Tim Gostony
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
# UICountingLabel ####
Adds animated counting support to `UILabel`.
![alt text](https://github.com/dataxpress/UICountingLabel/blob/master/demo.gif "demo")
## CocoaPods ######
UICountingLabel is available on CocoaPods.
Add this to your Podfile:
`pod 'UICountingLabel'`
And then run:
`$ pod install`
## Setup ######
Simply initialize a `UICountingLabel` the same way you set up a regular `UILabel`:
UICountingLabel* myLabel = [[UICountingLabel alloc] initWithFrame:CGRectMake(10, 10, 100, 40)];
[self.view addSubview:myLabel];
You can also add it to your XIB file, just make sure you set the class type to `UICountingLabel` instead of `UILabel` and be sure to `#import "UICountingLabel.h"` in the header file.
## Use #####
Set the format of your label. This will be filled with a single int or float (depending on how you format it) when it updates:
myLabel.format = @"%d";
Alternatively, you can provide a `UICountingLabelFormatBlock`, which permits greater control over how the text is formatted:
myLabel.formatBlock = ^NSString* (CGFloat value) {
NSInteger years = value / 12;
NSInteger months = (NSInteger)value % 12;
if (years == 0) {
return [NSString stringWithFormat: @"%ld months", (long)months];
}
else {
return [NSString stringWithFormat: @"%ld years, %ld months", (long)years, (long)months];
}
};
There is also a `UICountingLabelAttributedFormatBlock` to use an attributed string. If the `formatBlock` is specified, it takes precedence over the `format`.
Optionally, set the mode. The default is `UILabelCountingMethodEaseInOut`, which will start slow, speed up, and then slow down as it reaches the end. Other options are described below in the Methods section.
myLabel.method = UILabelCountingMethodLinear;
When you want the label to start counting, just call:
[myLabel countFrom:50 to:100];
You can also specify the duration. The default is 2.0 seconds.
[myLabel countFrom:50 to:100 withDuration:5.0f];
Additionally, there is `animationDuration` property which you can use to override the default animation duration.
myLabel.animationDuration = 1.0;
You can use common convinient methods for counting, such as:
[myLabel countFromCurrentValueTo:100];
[myLabel countFromZeroTo:100];
Behind the scenes, these convinient methods use one base method, which has the following full signature:
[myLabel countFrom:(float)startValue
to:(float)endValue
withDuration:(NSTimeInterval)duration];
You can get current value of your label using `-currentValue` method (works correctly in the process of animation too):
CGFloat currentValue = [myLabel currentValue];
Optionally, you can specify a `completionBlock` to perform an acton when the label has finished counting:
myLabel.completionBlock = ^{
NSLog(@"finished counting");
};
## Formats #####
When you set the `format` property, the label will look for the presence of `%(.*)d` or `%(.*)i`, and if found, will cast the value to `int` before formatting the string. Otherwise, it will format it using a `float`.
If you're using a `float` value, it's recommended to limit the number of digits with a format string, such as `@"%.1f"` for one decimal place.
Because it uses the standard `stringWithFormat:` method, you can also include arbitrary text in your format, such as `@"Points: %i"`.
## Modes #####
There are currently four modes of counting.
### `UILabelCountingMethodLinear` #####
Counts linearly from the start to the end.
### `UILabelCountingMethodEaseIn` #####
Ease In starts out slow and speeds up counting as it gets to the end, stopping suddenly at the final value.
### `UILabelCountingMethodEaseOut` #####
Ease Out starts out fast and slows down as it gets to the destination value.
### `UILabelCountingMethodEaseInOut` #####
Ease In/Out starts out slow, speeds up towards the middle, and then slows down as it approaches the destination. It is a nice, smooth curve that looks great, and is the default method.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, UILabelCountingMethod) {
UILabelCountingMethodEaseInOut,
UILabelCountingMethodEaseIn,
UILabelCountingMethodEaseOut,
UILabelCountingMethodLinear
};
typedef NSString* (^UICountingLabelFormatBlock)(CGFloat value);
typedef NSAttributedString* (^UICountingLabelAttributedFormatBlock)(CGFloat value);
@interface UICountingLabel : UILabel
@property (nonatomic, strong) NSString *format;
@property (nonatomic, assign) UILabelCountingMethod method;
@property (nonatomic, assign) NSTimeInterval animationDuration;
@property (nonatomic, copy) UICountingLabelFormatBlock formatBlock;
@property (nonatomic, copy) UICountingLabelAttributedFormatBlock attributedFormatBlock;
@property (nonatomic, copy) void (^completionBlock)(void);
-(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue;
-(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration;
-(void)countFromCurrentValueTo:(CGFloat)endValue;
-(void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration;
-(void)countFromZeroTo:(CGFloat)endValue;
-(void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration;
- (CGFloat)currentValue;
@end
#import <QuartzCore/QuartzCore.h>
#import "UICountingLabel.h"
#if !__has_feature(objc_arc)
#error UICountingLabel is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif
#pragma mark - UILabelCounter
#ifndef kUILabelCounterRate
#define kUILabelCounterRate 3.0
#endif
@protocol UILabelCounter<NSObject>
-(CGFloat)update:(CGFloat)t;
@end
@interface UILabelCounterLinear : NSObject<UILabelCounter>
@end
@interface UILabelCounterEaseIn : NSObject<UILabelCounter>
@end
@interface UILabelCounterEaseOut : NSObject<UILabelCounter>
@end
@interface UILabelCounterEaseInOut : NSObject<UILabelCounter>
@end
@implementation UILabelCounterLinear
-(CGFloat)update:(CGFloat)t
{
return t;
}
@end
@implementation UILabelCounterEaseIn
-(CGFloat)update:(CGFloat)t
{
return powf(t, kUILabelCounterRate);
}
@end
@implementation UILabelCounterEaseOut
-(CGFloat)update:(CGFloat)t{
return 1.0-powf((1.0-t), kUILabelCounterRate);
}
@end
@implementation UILabelCounterEaseInOut
-(CGFloat) update: (CGFloat) t
{
t *= 2;
if (t < 1)
return 0.5f * powf (t, kUILabelCounterRate);
else
return 0.5f * (2.0f - powf(2.0 - t, kUILabelCounterRate));
}
@end
#pragma mark - UICountingLabel
@interface UICountingLabel ()
@property CGFloat startingValue;
@property CGFloat destinationValue;
@property NSTimeInterval progress;
@property NSTimeInterval lastUpdate;
@property NSTimeInterval totalTime;
@property CGFloat easingRate;
@property (nonatomic, strong) CADisplayLink *timer;
@property (nonatomic, strong) id<UILabelCounter> counter;
@end
@implementation UICountingLabel
-(void)countFrom:(CGFloat)value to:(CGFloat)endValue {
if (self.animationDuration == 0.0f) {
self.animationDuration = 1.0f;
}
[self countFrom:value to:endValue withDuration:self.animationDuration];
}
-(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration {
self.startingValue = startValue;
self.destinationValue = endValue;
// remove any (possible) old timers
[self.timer invalidate];
self.timer = nil;
if (duration == 0.0) {
// No animation
[self setTextValue:endValue];
[self runCompletionBlock];
return;
}
self.easingRate = 3.0f;
self.progress = 0;
self.totalTime = duration;
self.lastUpdate = [NSDate timeIntervalSinceReferenceDate];
if(self.format == nil)
self.format = @"%f";
switch(self.method)
{
case UILabelCountingMethodLinear:
self.counter = [[UILabelCounterLinear alloc] init];
break;
case UILabelCountingMethodEaseIn:
self.counter = [[UILabelCounterEaseIn alloc] init];
break;
case UILabelCountingMethodEaseOut:
self.counter = [[UILabelCounterEaseOut alloc] init];
break;
case UILabelCountingMethodEaseInOut:
self.counter = [[UILabelCounterEaseInOut alloc] init];
break;
}
CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateValue:)];
timer.frameInterval = 2;
[timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
self.timer = timer;
}
- (void)countFromCurrentValueTo:(CGFloat)endValue {
[self countFrom:[self currentValue] to:endValue];
}
- (void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration {
[self countFrom:[self currentValue] to:endValue withDuration:duration];
}
- (void)countFromZeroTo:(CGFloat)endValue {
[self countFrom:0.0f to:endValue];
}
- (void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration {
[self countFrom:0.0f to:endValue withDuration:duration];
}
- (void)updateValue:(NSTimer *)timer {
// update progress
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
self.progress += now - self.lastUpdate;
self.lastUpdate = now;
if (self.progress >= self.totalTime) {
[self.timer invalidate];
self.timer = nil;
self.progress = self.totalTime;
}
[self setTextValue:[self currentValue]];
if (self.progress == self.totalTime) {
[self runCompletionBlock];
}
}
- (void)setTextValue:(CGFloat)value
{
if (self.attributedFormatBlock != nil) {
self.attributedText = self.attributedFormatBlock(value);
}
else if(self.formatBlock != nil)
{
self.text = self.formatBlock(value);
}
else
{
// check if counting with ints - cast to int
if([self.format rangeOfString:@"%(.*)d" options:NSRegularExpressionSearch].location != NSNotFound || [self.format rangeOfString:@"%(.*)i"].location != NSNotFound )
{
self.text = [NSString stringWithFormat:self.format,(int)value];
}
else
{
self.text = [NSString stringWithFormat:self.format,value];
}
}
}
- (void)setFormat:(NSString *)format {
_format = format;
// update label with new format
[self setTextValue:self.currentValue];
}
- (void)runCompletionBlock {
if (self.completionBlock) {
self.completionBlock();
self.completionBlock = nil;
}
}
- (CGFloat)currentValue {
if (self.progress >= self.totalTime) {
return self.destinationValue;
}
CGFloat percent = self.progress / self.totalTime;
CGFloat updateVal = [self.counter update:percent];
return self.startingValue + (updateVal * (self.destinationValue - self.startingValue));
}
@end
//
// WeakWebViewScriptMessageDelegate.m
// Open
//
// Created by 雷俊博 on 2019/8/19.
// Copyright © 2019 黄江涛 . All rights reserved.
//
#import "WeakWebViewScriptMessageDelegate.h"
@implementation WeakWebViewScriptMessageDelegate
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([self.scriptDelegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}
}
@end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment