您的位置:首页 > 产品设计 > UI/UE

UITableView的封装和类的交互1

2015-08-15 19:43 459 查看
思路:
如果我们的需求需要多个表视图来实现,那么我们如果将表视图的实现放在控制器中,那么这样代码的动态性将会降低,并且如果有多个表视图在同一个控制器,那么代码将会出现很多垃圾代码(也就是说功能实现,逻辑条理一团乱麻) 这种情况下,可以封装表视图,然后控制器需要做的事情只是给表视图数据并且将表视图添加到自己的根视图。
// 1. 首先我们需要创建一个容器视图 来装载tableView并且来实现tableView的数据源方法和代理方法
// 2. 提供接口完成类之间的交互

接下来需要做的事   容器视图不一定会只有一个子视图,如果有一个大的视图或者是比较独立的视图 我们需要继续封装 然后将其给予表视图或者容器视图等

实现起来不难,逻辑思路要清晰,注意细节,方可轻松实现封装。

1. 表视图的头视图封装一个可以定时滚动的滚动视图
2.表视图的实现是需要封装自定义单元格

// 一  表视图的定时滚动的滚动视图的封装

@interface YXADSView () <UIScrollViewDelegate>

@property (nonatomic,weak) UIScrollView     *scrollView;
@property (nonatomic,weak) UIPageControl    *pageControl;
@property (nonatomic,weak) NSTimer          *timer;

@end

+ (id)adsView {

return [[self alloc] init];

}

// 提出问题:封装UIView的时候,相关子控件的创建在哪里???
// 使用init方式系统会自动调用 - initWithFrame: 我们可以在这里创建相关的子控件等,但是不能在这里设置子控件的frame,因为这个时候还没有设置父视图(容器视图)的frame,子视图的frame怎么会设置成功呢!  所以我们就将创建的任务给- initWithFrame 而将设置视图frame或是其他属性的任务可以在- willMoveToSuperview: 来完成。当然这不是固定的,只不过这样编码的话,逻辑思路更加清晰。因为在你将子视图添加到父视图的时候,会调用这个方法,所以这个时候设置frame是很合适的,然后当父视图从控制器得到数据,我们只需要重写数据的set方法,将数据给到视图然后我们就可以完成的大概过程了!
- (id)initWithFrame:(CGRect)frame {

self = [super initWithFrame:frame];
// 默认 CGRectZero;
if (self) {
// 1.UIScrollView
UIScrollView *scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = self;
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.frame = self.bounds; // 0 0 0 0
scrollView.backgroundColor = [UIColor whiteColor];
self.scrollView = scrollView;
// 2.UIPageControl
UIPageControl *pageControl = [[UIPageControl alloc] init];
//        UIPageControl *pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
//        _pageControl.numberOfPages = 5; //self.images.count;
//        self.pageControl = pageControl; //  弱指针保存地址
//        [self addSubview:pageControl]; //   强指针保存对象所有权
[self addSubview:_pageControl = pageControl];
// 默认居中显示
_pageControl.pageIndicatorTintColor = [UIColor yellowColor];
_pageControl.currentPageIndicatorTintColor = [UIColor purpleColor];

// 子控件需要等父视图有了frame在设置自己的frame
- (void)willMoveToSuperview:(UIView *)newSuperview {

// 1.设置自己的frame值
CGFloat selfX = 0;
CGFloat selfY = 0;
CGFloat selfW = newSuperview.width;
CGFloat selfH = 220;
self.frame = CGRectMake(selfX, selfY, selfW, selfH);
self.backgroundColor = [UIColor lightGrayColor];

// 2.子控件参考父视图设置frame值
self.scrollView.frame = self.bounds;
self.pageControl.y =self.bounds.size.height-40;
self.pageControl.width = self.width;

}

}
return self;
}
// 我们这里为了避免或者减少代码的书写量
我们可以写一个UIView的类别 专门设置frame 这样我们调用起来就一劳永逸了

// [self.view addSubView : adsView];
- (void)setImages:(NSArray *)images {

_images = images;
// 设置依赖于数据的一些属性
self.pageControl.numberOfPages = images.count;
// 2. scrollView 的 contentSize
self.scrollView.contentSize = CGSizeMake(images.count*self.scrollView.width, self.scrollView.height);
// 给scrollView 添加子控件
for (int i = 0; i < images.count; i++) {

NSString *name = images[i];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[self.scrollView addSubview:button];
CGFloat btnW = self.scrollView.width;
CGFloat btnX = btnW * i;
CGFloat btnY = 0;
CGFloat btnH = self.scrollView.height;
button.frame = CGRectMake(btnX, btnY, btnW, btnH);
[button setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal];
[button addTarget:self action:@selector(btnTouch:) forControlEvents:UIControlEventTouchUpInside];
button.tag = i;
}

}

/**
*  如何封装?

1. 变化点
1.数据变化点 重写set
2.通信 代理 block

2. 确定一个容器 UIView

3. 逻辑搭建
1. UI搭建
2. UI逻辑
*/

下面是实现滚动视图的一些代理方法和定时器的使用

#pragma mark - 业务逻辑辅助功能
- (void)autoScroll {

int totalPage = 5;
// 当开始滚动的时候获取当前的pageControl的点,需要+1,出现临界点,需要跳到第一个点
NSInteger page = self.pageControl.currentPage >= totalPage-1 ? 0 :self.pageControl.currentPage+1;
// 滚动的时候,滚动视图的偏移量也需要发生变化  (随着pageControl的当前点)
self.scrollView.contentOffset = CGPointMake(self.scrollView.width * page , 0);
}

- (void)startTimer {

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(autoScroll) userInfo:nil repeats:YES];// 强指针指向timer内存 临时变量
self.timer = timer;           // 属性参数为strong,持有该对象
// 将一个timer对象加入到最主要的事件循环中
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
//    The receiver retains aTimer. To remove a timer from all run loop modes on which it is installed, send an invalidate message to the timer.
//    接收者保留了一个timer对象,如果想要移除已经存在于事件循环中的timer对象,需要发送invalidate消息

}

#pragma mark - UIScrollView Delegate
//  只要偏移量变化,就会调用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 1 获得偏移量
CGPoint contentOffset = scrollView.contentOffset;
// 2 计算当前为第几页
int page = (contentOffset.x + scrollView.width * 0.5)/ scrollView.width;
// 3 更改pagecontrol属性
self.pageControl.currentPage = page;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 1.当即将手动拖动scrollView做滑动的时候,取消timer事件
[self.timer invalidate];
//    This method is the only way to remove a timer from an NSRunLoop object.
// 这个方式是将一个timer从事件循环中移除的唯一途径
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
// 重新启动timer事件
// timer如果调用了invalidata之后就无法再重新启动这个事件   必须重新创建该事件
[self startTimer];
}

/**
*  initWithFrame       初始化子控件
moveToSuperView     设置frame
set                 给内容
*/
我们每个界面都不可能是死的界面,不仅可以接收数据并且显示。我们同样可以实现类与类之间的通信。

当我们点击滚动视图的图片时候,我们需要响应事件,然后告诉控制器或是别的类我将会和你通信,通信的实现就会用到了block或者是代理,还有通知,一般来说我们这样的类之间的通信都是一对一的,所以我们无需用通知,通知的使用一般情况下是在类之间一对多的情况下来使用。如果通知用的多的话,代码将会非常繁琐,而且维护性十分差。
#pragma mark - 按钮点击响应事件实现
b194
- (void)btnTouch:(UIButton *)button {

[_delegate adsViewDidSelected:self image:self.images[button.tag] index:button.tag];

if (self.adsViewDidSelectedBlock) {
self.adsViewDidSelectedBlock(self,self.images[button.tag],button.tag);
}
}

协议方法的声明和block

typedef void(^adsViewBlock)(YXADSView * ,NSString *,NSInteger);

@protocol YXADSViewDelegate <NSObject>
/**
*  当点击某个图片时会调用该方法
*
*  @param adsView 当前类对象
*  @param image   被点击的图片
*  @param index   被点击的图片的索引
*/
- (void)adsViewDidSelected:(YXADSView *)adsView
image:(NSString *)image
index:(NSInteger)index;

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: