您的位置:首页 > 其它

08-百思不得姐(第八天)

2016-04-17 10:03 190 查看

一、点击顶部的状态栏,scrollView会自动回滚到顶部



实现思路:

自定义一个View模型

提供显示顶部窗口的接口

在接口方法里面创建window,添加到顶部

给window添加点击手势,点击后scrollView回滚到顶部

1> 添加view模型,提供接口

@interface DSBackTopWindow : NSObject

/**
*  显示window
*/
+ (void)show;

/**
*  隐藏window
*/
+ (void)hide;

@end


2> 实现接口方法

创建window,并且添加手势,只需要创建一次,所以写在initialize方法里

+ (void)initialize
{
window_ = [[UIWindow alloc] init];
window_.frame = CGRectMake(0, 0, DSScreenW, DSSystemStatusBarH);
window_.windowLevel = UIWindowLevelAlert;
window_.backgroundColor = [UIColor clearColor];
[window_ addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(topWindowClick)]];
}


接口实现

/**
*  显示
*/
+ (void)show
{
window_.hidden = NO;
}

/**
*  隐藏
*/
+ (void)hide
{
window_.hidden = YES;
}


window点击手势监听方法的实现(首先找到keyWindow里面的scrollView,然后让其滚回到顶部)

/**
*  顶部window点击(scrollView回滚到顶部)
*/
+ (void)topWindowClick
{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[self searchScrollViewInSuperView:window];
}

/**
*  寻找主窗口的scrollView子控件(递归)
*/
+ (void)searchScrollViewInSuperView:(UIView *)superView
{
for (UIScrollView *subView in superView.subviews) {

// 只有显示在主窗口上,而且类型是scrollView才需要移动
if ([subView isKindOfClass:[UIScrollView class]] && subView.isShowingOnkeyWindow) {
CGPoint offset = subView.contentOffset;
offset.y = -subView.contentInset.top;

[subView setContentOffset:offset animated:YES];
}

[self searchScrollViewInSuperView:subView];
}
}


找里面的scrollView时,需要用到递归的思想,一层层往下遍历,而且找到的scrollView也必须是显示在keyWindow上的!所以给UIView分类提供一个方法:- (BOOL)isShowingOnkeyWindow; 用来判断某个UIView在不在主窗口上,以下是实现:

- (BOOL)isShowingOnkeyWindow
{
// 主窗口
UIWindow *window = [UIApplication sharedApplication].keyWindow;

// 以窗口左上角为坐标原地,计算self的矩形框
CGRect newFrame = [window convertRect:self.frame fromView:self.superview];
CGRect windowBounds = window.bounds;

// 主窗口的bounds是否 和 self的矩形框有重叠
BOOL isIntersects = CGRectIntersectsRect(newFrame, windowBounds);

// 列举self在主窗口的几个必要条件
return !self.hidden && self.window == window && self.alpha > 0.01 && isIntersects;

}


判断的时候,必须得转换坐标系,而且得符合几个必须条件:

1、该View不是隐藏的

2、该View是有主窗口的

3、该View是可见的,也就是alpha > 0.01

4、该view和主窗口有重叠

到这里,第一个需求就完成了!

二、点击评论cell时,弹出选项条



如果想实现这个效果,得用到apple自带的一个类(UIMenuController)

以下是该类的具体使用:

1> UIMenuController须知

默认情况下, 有以下控件已经支持UIMenuController

UITextField

UITextView

UIWebView

2> 让其他控件也支持UIMenuController(比如UILabel)

自定义UILabel

重写2个方法

/**
* 让label有资格成为第一响应者
*/
- (BOOL)canBecomeFirstResponder
{
return YES;
}

/**
* label能执行哪些操作(比如copy, paste等等)
* @return  YES:支持这种操作
*/
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(paste:)) return YES;

return NO;
}


实现各种操作方法

- (void)cut:(UIMenuController *)menu
{
// 将自己的文字复制到粘贴板
[self copy:menu];

// 清空文字
self.text = nil;
}

- (void)copy:(UIMenuController *)menu
{
// 将自己的文字复制到粘贴板
UIPasteboard *board = [UIPasteboard generalPasteboard];
board.string = self.text;
}

- (void)paste:(UIMenuController *)menu
{
// 将粘贴板的文字 复制 到自己身上
UIPasteboard *board = [UIPasteboard generalPasteboard];
self.text = board.string;
}


让label成为第一响应者

// 这里的self是label
[self becomeFirstResponder];


显示显示UIMenuController

UIMenuController *menu = [UIMenuController sharedMenuController];
// targetRect: MenuController需要指向的矩形框
// targetView: targetRect会以targetView的左上角为坐标原点
[menu setTargetRect:self.bounds inView:self];
// [menu setTargetRect:self.frame inView:self.superview];
[menu setMenuVisible:YES animated:YES];


3> 自定义UIMenuController内部的Item

添加item

// 添加MenuItem(点击item, 默认会调用控制器的方法)
UIMenuItem *ding = [[UIMenuItem alloc] initWithTitle:@"顶" action:@selector(ding:)];
UIMenuItem *replay = [[UIMenuItem alloc] initWithTitle:@"回复" action:@selector(replay:)];
UIMenuItem *report = [[UIMenuItem alloc] initWithTitle:@"举报" action:@selector(report:)];
menu.menuItems = @[ding, replay, report];


所以很明显,我们的需求应该就是自定义MenuItem!

接下来在点击cell的方法中弹出选项条

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIMenuController *menu = [UIMenuController sharedMenuController];

if (menu.isMenuVisible) {
// 如果menu是显示的,再次点击就隐藏
[menu setMenuVisible:NO animated:YES];
return;
}else {
// 取出cell
DSCommentCell *cell = [tableView cellForRowAtIndexPath:indexPath];

// 让cell变成第一响应者
[cell becomeFirstResponder];

// 添加UIMenuItem(点击item,默认会调用控制器的方法)
UIMenuItem *ding = [[UIMenuItem alloc] initWithTitle:@"顶" action:@selector(ding:)];
UIMenuItem *replay = [[UIMenuItem alloc] initWithTitle:@"回复" action:@selector(replay:)];
UIMenuItem *report = [[UIMenuItem alloc] initWithTitle:@"举报" action:@selector(report:)];
menu.menuItems = @[ding, replay, report];

CGRect targetRect = CGRectMake(0, cell.height * 0.5, cell.width, cell.height * 0.5);

/*
targetRect : UIMenuController指向的矩形框
targetView : targetRect会以targetView的左上角为坐标原点
*/
[menu setTargetRect:targetRect inView:cell];
[menu setMenuVisible:YES animated:YES];
}

}


由于是让cell支持UIMenuController,所以重写的两个方法得写在自定义cell里面!

三、点击tabbar,scrollView会自动回滚到顶部



在很多APP里面,一般都会有点击tabbar后,当前页面会自动刷新!

实现思路:

让AppDelegate成为tabbarController的代理

// 设置根控制器
DSTabBarController *tabbarVc = [[DSTabBarController alloc] init];
tabbarVc.delegate = self;
self.window.rootViewController = tabbarVc;


然后实现代理方法- (void)tabBarController:(UITabBarController )tabBarController didSelectViewController:(UIViewController )viewController,在该代理方法中发出通知

#pragma mark - UITabBarControllerDelegate
/**
*  监听tabbar的选中
*/
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
[DSNotificationCenter postNotificationName:DSTabBarDidSelectedNotification object:nil userInfo:nil];
}


接着在DSBaseViewController里面监听通知

// 监听tabbar的选中通知
[DSNotificationCenter addObserver:self selector:@selector(tabBarSelect) name:DSTabBarDidSelectedNotification object:nil];


实现监听方法,在监听方法中监听tabbar的选中

- (void)tabBarSelect
{
// 判断是不是同一次点击,而且点击的控制器view在不在keywindow上
if (self.lastSelectedIndex == self.tabBarController.selectedIndex && self.tableView.isShowingOnkeyWindow) {
// 头部开始刷新
[self.tableView.mj_header beginRefreshing];
}

// 保存当前选中的index(控制器)
self.lastSelectedIndex = self.tabBarController.selectedIndex;
}


四、实现新帖控制器内容



由于新帖的展示内容和精华是一模一样的(唯一的不同点就是请求参数的不同),所以考虑用继承来完成!

实现思路:

1> 让DSNewViewController继承自DSEssenceViewController

#import "DSEssenceViewController.h"

@interface DSNewViewController : DSEssenceViewController

@end


2> 设置请求参数

- (NSString *)a
{
return [self.parentViewController isKindOfClass:[DSNewViewController class]] ? @"newlist" : @"list";
}


这里得判断父控制器是不是这个类型或者继承自这个类型,所以这个控制器的类型不能是DSEssenceViewController,因为DSNewViewController是继承自DSEssenceViewController的,所以条件也会成立!因此得用DSNewViewController,这个时候就完成了新帖的界面!

接着会出现一个Bug,那就是当底部评论为0的时候,点击评论界面会崩溃,报以下错误:



这个错误很明显,调用了NSArray的objectForKeyedSubscript方法,但是方法找不到!因为objectForKeyedSubscript是字典的方法!而且出错的地方就是评论控制器的loadNewComments方法里面的block!

其实这个错误是由于服务器引起的,因为服务器在数据为nil的时候,返回的是一个数组,而不是字典,这就是导致报错的原因!所以解决办法很简单:

// 说明没有数据
if (![responseObject isKindOfClass:[NSDictionary class]]) {
// 结束刷新
[self.tableView.mj_header endRefreshing];
return ;
}


五、实现我的界面



该界面主要就是一个tableView(两个cell + 一个自定义的tableFooterView),关于cell就不多说了,主要说一下底部的自定义tableFooterView!

1> 自定义DSMeFooterView,继承自UIView,然后在控制器中设置给tableView的tableFooterView属性

// 设置footer
DSMeFooterView *footer = [[DSMeFooterView alloc] init];
self.tableView.tableFooterView = footer;


2> 在自定义DSMeFooterView添加按钮,在内部发送请求,获取里面按钮的图片和文字,以及点击后需要跳转的url

- (instancetype)initWithFrame:(CGRect)frame
{
if (self  = [super initWithFrame:frame]) {

// 请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"a"] = @"square";
params[@"c"] = @"topic";

// 发送请求
[[AFHTTPSessionManager manager] GET:@"http://api.budejie.com/api/api_open.php" parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary *  _Nullable responseObject) {

// 字典数组 -> 模型数组
NSArray *squares = [DSSquare mj_objectArrayWithKeyValuesArray:responseObject[@"square_list"]];

// 创建按钮
[self createSquares:squares];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}

return self;
}

- (void)createSquares:(NSArray *)squares
{
// 方块数量
NSInteger squareCount = squares.count;
// 最大列数
NSInteger maxCols = 4;

CGFloat buttonW = self.width / maxCols;
CGFloat buttonH = buttonW;

for (int i = 0; i < squareCount; i++) {
// 取出模型
DSSquare *square = squares[i];

DSSquareButton *btn = [[DSSquareButton alloc] init];
btn.square = square;
[btn addTarget:self action:@selector(btnClick:) forControlEvents:(UIControlEventTouchUpInside)];

NSInteger col = i % maxCols;
NSInteger row = i / maxCols;

btn.width = buttonW;
btn.height = buttonH;
btn.x = buttonW * col;
btn.y = buttonH * row;

[self addSubview:btn];

// 计算footer高度
//        self.height = CGRectGetMaxY(btn.frame);
}

// 总页数 = (总个数 + 每页最大数 - 1) / 每页最大数
NSInteger rows = (squareCount + maxCols - 1) / maxCols;
self.height = rows * buttonH;
}


在创建的时候就发送请求获取服务器数据,然后创建按钮。由于按钮的样式和默认是不一样的,所以按钮也需要自定义!首先传递数据模型,然后实现内部的细节处理!

/**
*   初始化子控件
*/
- (void)setup
{
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.titleLabel.font = [UIFont systemFontOfSize:10];
[self setBackgroundImage:[UIImage imageNamed:@"mainCellBackground"] forState:(UIControlStateNormal)];
[self setTitleColor:[UIColor blackColor] forState:(UIControlStateNormal)];

}

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}

return self;
}

- (void)awakeFromNib
{
[self setup];
}

- (void)layoutSubviews
{
[super layoutSubviews];

// 设置imageView
self.imageView.width = self.width * 0.5;
self.imageView.height = self.height * 0.6;
self.imageView.x = (self.width - self.imageView.width) * 0.5;
self.imageView.y = self.height * 0.15;

// 设置titleLabel
self.titleLabel.x = 0;
self.titleLabel.y = CGRectGetMaxY(self.imageView.frame);
self.titleLabel.width = self.width;
self.titleLabel.height = self.height - self.titleLabel.y;
}

- (void)setSquare:(DSSquare *)square
{
_square = square;

// 设置文字和图片
[self setTitle:square.name forState:(UIControlStateNormal)];
[self sd_setImageWithURL:[NSURL URLWithString:square.icon] forState:(UIControlStateNormal)];
}


3> 点击按钮,实现url的跳转

首先得创建一个DSWebViewController,然后添加webView

- (void)btnClick:(DSSquareButton *)button
{
// 如果前缀不是http
if (![button.square.url hasPrefix:@"http"]) return;

// 传递数据
DSWebViewController *webVc = [[DSWebViewController alloc] init];
webVc.title = button.square.name;
webVc.url = button.square.url;

// push网页控制器
UITabBarController *tabBar = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;
UINavigationController *nav = tabBar.selectedViewController;
[nav pushViewController:webVc animated:YES];
}


push的时候把title和url都传递过去,想要拿到当前的nav控制器,首先需要拿到tabbarController,然后拿到选中的nav控制器,就可以实现跳转了!

4> 在DSWebViewController的底部添加工具条,用来控制器网页的前进和后退,以及刷新!



以下是控制器代码:

- (void)viewDidLoad {
[super viewDidLoad];

self.webView.delegate = self;

[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.url]]];
}

/**
*  上一页
*/
- (IBAction)goBack:(id)sender {

[self.webView goBack];
}

/**
*  下一页
*/
- (IBAction)goForward:(id)sender {

[self.webView goForward];
}

/**
*  刷新
*/
- (IBAction)refresh:(id)sender {
[self.webView reload];
}

#pragma mark - <UIWebViewDelegate>
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.goBackItem.enabled = webView.canGoBack;
self.goForward.enabled = webView.canGoForward;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: