源代码
链接:
https://pan.baidu.com/s/1JmQyOeTj02f8Yo99WW2XZA 密码:mv2h
主要代码:
//
// ViewController.m
// 网易栏目分类
//
// Created by 许磊 on 2019/2/25.
// Copyright © 2019年 xulei. All rights reserved.
//
#import "ViewController.h"
#import "ChangeColumnView.h"
#import "Constants.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/**布局*/
[self UIInit];
}
#pragma mark -------UIInit ---------
//界面布局
-(void)UIInit{
//导航栏
{
//背景颜色
self.navigationController.navigationBar.barTintColor = [UIColor redColor];
//标题
self.title = @"网易新闻";
self.navigationController.navigationBar.titleTextAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"Helvetica-Bold" size:17],NSForegroundColorAttributeName:[UIColor blackColor]};
//左边的按钮
UIImage *image = [[UIImage imageNamed:@"arrow_0"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIBarButtonItem *listChangeButton = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:@selector(listButtonDidClicked:)];
listChangeButton.tintColor = [UIColor blackColor];
self.navigationItem.leftBarButtonItem = listChangeButton;
//右边的按钮
UIBarButtonItem *columnChangeButton = [[UIBarButtonItem alloc] initWithTitle:@"编辑" style:UIBarButtonItemStylePlain target:self action:@selector(editButtonDidClicked:)];
columnChangeButton.tintColor = [UIColor blackColor];
self.navigationItem.rightBarButtonItem = columnChangeButton;
}
}
#pragma mark -------listButtonDidClicked: ---------
//列表平铺按钮被点击
-(void)listButtonDidClicked:(UIBarButtonItem *)sender{
//Tag值判断按钮目前的状态 0-隐藏 1-显示
if (sender.tag == 0) {
//改变状态
sender.tag = 1;
//动画
[UIView animateWithDuration:0.5 animations:^{
sender.image = [UIImage imageNamed:@"arrow_1"];
}];
//显示
[ChangeColumnView showColumnViewWithFrame:CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height-64)];
} else{
//改变状态
sender.tag = 0;
//动画
[UIView animateWithDuration:0.5 animations:^{
sender.image = [UIImage imageNamed:@"arrow_0"];
}];
//隐藏
[ChangeColumnView hideColumnView];
}
}
#pragma mark -------editButtonDidClicked: ---------
//编辑按钮被点击
-(void)editButtonDidClicked:(UIBarButtonItem *)sender{
BOOL isEditing;
//按钮文本改变
if ([sender.title isEqualToString:@"编辑"]) {
//编辑状态
sender.title = @"完成";
isEditing = YES;
} else{
//正常状态
sender.title = @"编辑";
isEditing = NO;
}
//发送消息
[[NSNotificationCenter defaultCenter] postNotificationName:kEditStatusNotificationName object:@(isEditing)];
}
@end
//
// Constants.h
// 网易分类
//
// Created by 许磊 on 2019/2/25.
// Copyright © 2019年 xulei. All rights reserved.
//
/**常量性东西 保持一致*/
#ifndef Constants_h
#define Constants_h
//消息的名称
#define kEditStatusNotificationName @"kEditStatusNotificationName"
//记录栏目的类型
typedef enum {
kOperationTypeSelected,
kOperationTypeMore
} kOperationType;
#endif /* Constants_h */
//
// ChangeColumnView.h
// 网易分类
//
// Created by 许磊 on 2019/2/24.
// Copyright © 2019年 xulei. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "CreatColumnItem.h"
@interface ChangeColumnView : UIView
/**提供一个类方法---显示视图*/
+(void)showColumnViewWithFrame:(CGRect)frame;
/**提供一个类方法---隐藏视图*/
+(void)hideColumnView;
/**按钮方法声明*/
-(void)itemDeleteButtonDidClicked:(UIButton *)sender;
-(void)itemButtonDidClicked:(CreatColumnItem *)sender;
@end
//
// ChangeColumnView.m
// 网易分类
//
// Created by 许磊 on 2019/2/24.
// Copyright © 2019年 xulei. All rights reserved.
//
#import "ChangeColumnView.h"
#import "AppDelegate.h"
#import "OperationView.h"
//静态 单例模式
static ChangeColumnView *instance = nil;
@interface ChangeColumnView ()
/**管理已经显示的栏目的视图*/
@property (nonatomic,strong) OperationView *selectedOperationView;
/**管理没有显示的栏目的视图*/
@property (nonatomic,strong) OperationView *moreOperationView;
/**保存栏目数据*/
@property (nonatomic,strong) NSMutableArray *selectedArray;
@property (nonatomic,strong) NSMutableArray *moreArray;
@end
@implementation ChangeColumnView
//为了布局 重新initWithFrame
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self != nil) {
self.backgroundColor = [UIColor colorWithRed:221/255.0 green:221/225.0 blue:221/255.0 alpha:1];
//创建已经显示栏目视图
self.selectedOperationView = [[OperationView alloc] initWithFrame: CGRectZero];
_selectedOperationView.backgroundColor = [UIColor whiteColor];
_selectedOperationView.type = kOperationTypeSelected;
[self addSubview:_selectedOperationView];
//创建还没有添加的栏目视图
self.moreOperationView = [[OperationView alloc] initWithFrame:CGRectZero];
_moreOperationView.backgroundColor = [UIColor whiteColor];
_moreOperationView.type = kOperationTypeMore;
[self addSubview:_moreOperationView];
//加载数据
[self loadData];
}
return self;
}
#pragma mark -------loadData ---------
//加载数据
-(void)loadData{
//初始化数组
self.selectedArray = [NSMutableArray arrayWithArray:@[@"体育", @"房产", @"NBA", @"娱乐", @"科技", @"游戏", @"军事", @"直播", @"音乐"]];
self.moreArray = [NSMutableArray arrayWithArray:@[@"历史", @"影视", @"时尚", @"财经", @"数码", @"汽车"]];
//传递数据
self.selectedOperationView.itemNameArray = _selectedArray;
self.moreOperationView.itemNameArray = _moreArray;
//对子视图进行布局
[self layoutIfNeeded];
}
#pragma mark -------layoutSubviews ---------
//布局子视图
//根据数据对子视图重新布局 frame
-(void)layoutSubviews{
//计算选择的栏目视图的frame -> height
NSInteger row1 = (_selectedArray.count-1)/4+1;
self.selectedOperationView.frame = CGRectMake(0, 30, self.frame.size.width, row1*50+10);
//计算没有选择的栏目视图的frame -> height
NSInteger row2 = (_moreArray.count-1)/4+1;
self.moreOperationView.frame = CGRectMake(0, _selectedOperationView.frame.origin.y+_selectedOperationView.frame.size.height+30, self.frame.size.width, row2*50);
}
#pragma mark -------showColumnView ---------
//显示视图
+(void)showColumnViewWithFrame:(CGRect)frame{
//创建这个对象
instance = [[ChangeColumnView alloc] initWithFrame:frame];
instance.frame = CGRectMake(0, frame.origin.y, frame.size.width, 1);
//类方法不能访问其实例对象的属性
//1.找到应用程序的代理
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
//2.得到window对应的视图控制器的view
[appDelegate.window.rootViewController.view addSubview:instance];
//动画方式 往下平铺
[UIView animateWithDuration:0.6 animations:^{
instance.frame = frame;
}];
}
#pragma mark -------hideColumnView ---------
//隐藏视图
+(void)hideColumnView{
//动画方式 往上平铺
[UIView animateWithDuration:0.6 animations:^{
instance.frame = CGRectMake(0, instance.frame.origin.y, instance.frame.size.width, 1);
}completion:^(BOOL finished){
[instance removeFromSuperview];
}];
}
#pragma mark -------itemDeleteButtonDidClicked: ---------
//item上删除按钮的事件
-(void)itemDeleteButtonDidClicked:(UIButton *)sender{
NSLog(@"删除按钮");
//需要删除栏目
CreatColumnItem *columnButton = (CreatColumnItem *)sender.superview;
//1.获取点击的item在数组里面的索引值
NSInteger index = [self.selectedArray indexOfObject:columnButton.titleLabel.text];
//2.删除selected里面的index对象
[self.selectedArray removeObjectAtIndex:index];
//3.在more 里面添加这个对象
[self.moreArray addObject:columnButton .titleLabel.text];
//4.将这个item移动到上方
//传递数据
self.selectedOperationView.itemNameArray = _selectedArray;
self.moreOperationView.itemNameArray = _moreArray;
[self setNeedsLayout];
[self layoutIfNeeded];
}
#pragma mark -------itemButtonDidClicked: ---------
//item按钮被点击
-(void)itemButtonDidClicked:(CreatColumnItem *)sender{
NSLog(@"按钮本身");
if (sender.type == kOperationTypeMore) {
//需要添加栏目
//1.获取点击的item在数组里面的索引值
NSInteger index = [self.moreArray indexOfObject:sender.titleLabel.text];
//2.删除more里面的index对象
[self.moreArray removeObjectAtIndex:index];
//3.在selected里面添加这个对象
[self.selectedArray addObject:sender.titleLabel.text];
//4.将这个item移动到上方
//传递数据
self.selectedOperationView.itemNameArray = _selectedArray;
self.moreOperationView.itemNameArray = _moreArray;
[self setNeedsLayout];
[self layoutIfNeeded];
}
}
@end
//
// ChangeColumnView.m
// 网易分类
//
// Created by 许磊 on 2019/2/24.
// Copyright © 2019年 xulei. All rights reserved.
//
#import "ChangeColumnView.h"
#import "AppDelegate.h"
#import "OperationView.h"
//静态 单例模式
static ChangeColumnView *instance = nil;
@interface ChangeColumnView ()
/**管理已经显示的栏目的视图*/
@property (nonatomic,strong) OperationView *selectedOperationView;
/**管理没有显示的栏目的视图*/
@property (nonatomic,strong) OperationView *moreOperationView;
/**保存栏目数据*/
@property (nonatomic,strong) NSMutableArray *selectedArray;
@property (nonatomic,strong) NSMutableArray *moreArray;
@end
@implementation ChangeColumnView
//为了布局 重新initWithFrame
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self != nil) {
self.backgroundColor = [UIColor colorWithRed:221/255.0 green:221/225.0 blue:221/255.0 alpha:1];
//创建已经显示栏目视图
self.selectedOperationView = [[OperationView alloc] initWithFrame: CGRectZero];
_selectedOperationView.backgroundColor = [UIColor whiteColor];
_selectedOperationView.type = kOperationTypeSelected;
[self addSubview:_selectedOperationView];
//创建还没有添加的栏目视图
self.moreOperationView = [[OperationView alloc] initWithFrame:CGRectZero];
_moreOperationView.backgroundColor = [UIColor whiteColor];
_moreOperationView.type = kOperationTypeMore;
[self addSubview:_moreOperationView];
//加载数据
[self loadData];
}
return self;
}
#pragma mark -------loadData ---------
//加载数据
-(void)loadData{
//初始化数组
self.selectedArray = [NSMutableArray arrayWithArray:@[@"体育", @"房产", @"NBA", @"娱乐", @"科技", @"游戏", @"军事", @"直播", @"音乐"]];
self.moreArray = [NSMutableArray arrayWithArray:@[@"历史", @"影视", @"时尚", @"财经", @"数码", @"汽车"]];
//传递数据
self.selectedOperationView.itemNameArray = _selectedArray;
self.moreOperationView.itemNameArray = _moreArray;
//对子视图进行布局
[self layoutIfNeeded];
}
#pragma mark -------layoutSubviews ---------
//布局子视图
//根据数据对子视图重新布局 frame
-(void)layoutSubviews{
//计算选择的栏目视图的frame -> height
NSInteger row1 = (_selectedArray.count-1)/4+1;
self.selectedOperationView.frame = CGRectMake(0, 30, self.frame.size.width, row1*50+10);
//计算没有选择的栏目视图的frame -> height
NSInteger row2 = (_moreArray.count-1)/4+1;
self.moreOperationView.frame = CGRectMake(0, _selectedOperationView.frame.origin.y+_selectedOperationView.frame.size.height+30, self.frame.size.width, row2*50);
}
#pragma mark -------showColumnView ---------
//显示视图
+(void)showColumnViewWithFrame:(CGRect)frame{
//创建这个对象
instance = [[ChangeColumnView alloc] initWithFrame:frame];
instance.frame = CGRectMake(0, frame.origin.y, frame.size.width, 1);
//类方法不能访问其实例对象的属性
//1.找到应用程序的代理
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
//2.得到window对应的视图控制器的view
[appDelegate.window.rootViewController.view addSubview:instance];
//动画方式 往下平铺
[UIView animateWithDuration:0.6 animations:^{
instance.frame = frame;
}];
}
#pragma mark -------hideColumnView ---------
//隐藏视图
+(void)hideColumnView{
//动画方式 往上平铺
[UIView animateWithDuration:0.6 animations:^{
instance.frame = CGRectMake(0, instance.frame.origin.y, instance.frame.size.width, 1);
}completion:^(BOOL finished){
[instance removeFromSuperview];
}];
}
#pragma mark -------itemDeleteButtonDidClicked: ---------
//item上删除按钮的事件
-(void)itemDeleteButtonDidClicked:(UIButton *)sender{
NSLog(@"删除按钮");
//需要删除栏目
CreatColumnItem *columnButton = (CreatColumnItem *)sender.superview;
//1.获取点击的item在数组里面的索引值
NSInteger index = [self.selectedArray indexOfObject:columnButton.titleLabel.text];
//2.删除selected里面的index对象
[self.selectedArray removeObjectAtIndex:index];
//3.在more 里面添加这个对象
[self.moreArray addObject:columnButton .titleLabel.text];
//4.将这个item移动到上方
//传递数据
self.selectedOperationView.itemNameArray = _selectedArray;
self.moreOperationView.itemNameArray = _moreArray;
[self setNeedsLayout];
[self layoutIfNeeded];
}
#pragma mark -------itemButtonDidClicked: ---------
//item按钮被点击
-(void)itemButtonDidClicked:(CreatColumnItem *)sender{
NSLog(@"按钮本身");
if (sender.type == kOperationTypeMore) {
//需要添加栏目
//1.获取点击的item在数组里面的索引值
NSInteger index = [self.moreArray indexOfObject:sender.titleLabel.text];
//2.删除more里面的index对象
[self.moreArray removeObjectAtIndex:index];
//3.在selected里面添加这个对象
[self.selectedArray addObject:sender.titleLabel.text];
//4.将这个item移动到上方
//传递数据
self.selectedOperationView.itemNameArray = _selectedArray;
self.moreOperationView.itemNameArray = _moreArray;
[self setNeedsLayout];
[self layoutIfNeeded];
}
}
@end
//
// OperationView.m
// 网易分类
//
// Created by 许磊 on 2019/2/25.
// Copyright © 2019年 xulei. All rights reserved.
//
#import "OperationView.h"
#import "CreatColumnItem.h"
#import "Constants.h"
#define kPadding ((self.frame.size.width-20-70*4)/3)
@interface OperationView ()
/**存储创建的栏目视图*/
@property (nonatomic,strong) NSMutableArray *itemViewsArray;
/**记录编辑的状态*/
@property (nonatomic,assign) BOOL isEditing;
@end
@implementation OperationView
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self != nil) {
//监听状态变化的消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusChanged:) name:kEditStatusNotificationName object:nil];
}
return self;
}
#pragma mark -------statusChanged: ---------
//状态切换的消息事件
-(void)statusChanged:(NSNotification *)notifi{
NSLog(@"%@",notifi);
//解析消息的内容
self.isEditing = [notifi.object boolValue];
}
//重写set方法
-(void)setItemNameArray:(NSMutableArray *)itemNameArray{
_itemNameArray = itemNameArray;
//全部去除
for (CreatColumnItem *item in self.subviews) {
[item removeFromSuperview];
}
//初始化数组
self.itemViewsArray = [NSMutableArray array];
for (NSString *itemName in _itemNameArray) {
//创建所有的栏目视图
CreatColumnItem *item = [[CreatColumnItem alloc] initWithFrame:CGRectZero];
item.itemName = itemName;
item.type = self.type;
item.isEditing = _isEditing;
[self addSubview:item];
//添加到数组中
[self.itemViewsArray addObject:item];
}
//布局子视图
[self layoutIfNeeded];
}
#pragma mark -------layoutSubviews ---------
-(void)layoutSubviews{
for (int i = 0; i < _itemViewsArray.count; i++) {
//创建栏目 第几行 第几列
NSInteger row = i/4;
NSUInteger colum = i%4;
//取出对象
CreatColumnItem *item = [_itemViewsArray objectAtIndex:i];
//更改frame
item.frame = CGRectMake(10+(70+kPadding)*colum, 10+(30+20)*row, 70, 30);
}
}
@end
//
// CreatColumnItem.h
// 网易分类
//
// Created by 许磊 on 2019/2/25.
// Copyright © 2019年 xulei. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "Constants.h"
@interface CreatColumnItem : UIButton
/**接受数据-外部传来的栏目名称*/
@property (nonatomic,strong) NSString *itemName;
/**记录当前编辑的状态*/
@property (nonatomic,assign) BOOL isEditing;
/**记录这个栏目按钮在哪个栏目里面*/
@property (nonatomic,assign) kOperationType type;
@end
//
// CreatColumnItem.m
// 网易分类
//
// Created by 许磊 on 2019/2/25.
// Copyright © 2019年 xulei. All rights reserved.
//
#import "CreatColumnItem.h"
#import "ChangeColumnView.h"
@interface CreatColumnItem ()
/**删除按钮*/
@property (nonatomic,strong) UIButton *deleteBtn;
@end
@implementation CreatColumnItem
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self != nil) {
//设置背景图片
[self setBackgroundImage:[UIImage imageNamed:@"ring"] forState:UIControlStateNormal];
//字体颜色
[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//添加删除按钮
self.deleteBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_deleteBtn.frame = CGRectMake(-3, -2, 15, 15);
[_deleteBtn setImage:[UIImage imageNamed:@"delete"] forState:UIControlStateNormal];
_deleteBtn.hidden = YES;
[_deleteBtn addTarget:self.superview.superview action:@selector(itemDeleteButtonDidClicked:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_deleteBtn];
//给背景按钮添加消息
[self addTarget:self.superview.superview action:@selector(itemButtonDidClicked:) forControlEvents:UIControlEventTouchUpInside];
//监听状态改变的消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusChanged:) name:kEditStatusNotificationName object:nil];
}
return self;
}
#pragma mark -------statusChanged: ---------
//状态切换的消息事件
-(void)statusChanged:(NSNotification *)notifi{
NSLog(@"%@",notifi);
//解析消息的内容
self.isEditing = [notifi.object boolValue];
}
-(void)setItemName:(NSString *)itemName{
_itemName = itemName;
//设置文本
[self setTitle:_itemName forState:UIControlStateNormal];
}
-(void)setIsEditing:(BOOL)isEditing{
_isEditing = isEditing;
if (self.type == kOperationTypeSelected) {
//显现
_deleteBtn.hidden = !_isEditing;
}
}
@end
运行结果
附一:开发流程
- 1.创建一个按钮用来显示或者隐藏栏目,并添加事件(通过Tag值来区分状态 0隐藏 1显示)
-
2.实现和隐藏栏目视图
a.创建继承于UIView的ColumnView用于管理栏目
b.提供show和hide方法用于快速便捷的显示和隐藏这个栏目视图(show需要找到当前程序的唯一代理AppDelegate找到里面的rootViewController.view作为栏目视图的父视图)
c.添加和显示隐藏的动画(更改视图的高度达到上拉下铺的效果) -
3.创建OperationView用于管理每一个栏目按钮
a.已经选择和没有选择的栏目视图
b.加载数据
c.实现layoutSubViews,对当前的子视图进行重新布局
(
自动调用:父视图的frame改变了,其layoutSubviews会被自动调用
将试图A添加到某个视图B上 这个A视图的layoutSubviews会被自动调用
手动调用:setNeedsDisplay layoutIfNeeded
补充一:系统自动调用layoutSubviews方法
1.初始化不会触发layoutSubviews,但是如果设置了不为CGRectZero的frame的时候就会触发。
2.addSubview会触发layoutSubviews
3.设置view的frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4.滚动一个UIScrollView会触发layoutSubviews
5.旋转Screen会触发父UIView上的layoutSubviews事件
6.改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
补充二:手动刷新子对象布局
1.-layoutSubviews
方法:这个方法,默认没有做任何事情,需要子类进行重写
2.-setNeedsLayout
方法: 标记为需要重新布局,不立即刷新(下一次cycle立即刷新),但layoutSubviews
一定会被调用,异步调用layoutIfNeeded
立即刷新布局
3.-layoutIfNeeded
方法:如果有需要刷新的标记,立即调用layoutSubviews
进行布局(如果没有标记,不会调用layoutSubviews
)
4.如果要立即刷新,要先调用[view setNeedsLayout]
,把标记设为需要布局,然后马上调用[view layoutIfNeeded]
,实现布局
5.在视图第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded]
)
- 4.创建ColumnItem继承于UIButton
- 5.相关说明:
a.利用type区分上下两个不同的OperationView
b.利用block或者delegate一级一级传递数据
c.利用消息中心传递数据
d.我们采用的是事件加到self.superview.superview身上,而不是按钮本身self
附二:单例方法
- 目的是为了 确保整个程序中操作的都是这个对象。一般用于类方法,类方法调用简单。
- 大家共享的,能够一直存在的,使用static静态的关键字
static ChangeColumnView *instance = nil;
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self != nil) {
}
return self;
}