内外分离接口依赖及UIScrollView知识点
1:Class Extension 还能巧妙的解决一个接口暴露问题
有些属性或者方法对外可以提供,有些只针对内部的类进行调用;
// Sark.framework/Sark.h @interface Sark : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *creditCardPassword; // secret! @end // Sark.framework/PrivateSarkWife.h @interface PrivateSarkWife : NSObject - (void)robAllMoneyFromCreditCardOfSark:(Sark *)sark; // needs password! @end
假设 Sark.h 是 Sark.framework 唯一暴露的 Header,而 framework 中的一个私有类需要获取这个公共类的某个属性(或方法)该怎么办?PrivateSarkWife为Sark.framework内部一个类;上面的 creditCardPassword 属性需要一个对外不可见而对内可见的地方声明,这时候可以利用 Class Extension:
// Sark.h @interface Sark : NSObject @property (nonatomic, copy) NSString *name; @end // Sark+Internal.h <--- new @interface Sark () @property (nonatomic, copy) NSString *creditCardPassword; @end // Sark.m #import "Sark.h" #import "Sark+Internal.h" // <--- new
将对公业务和对私业务用 Class Extension 的形式拆到两个 Header 中,在Sark.m里把两个头文件都进行引用,CreditCardPassword被定义在Sark+Internal.h的头文件里面,这样私有类对私有属性的依赖就被成功隔离开了:
// PrivateSarkWife.m #import "PrivateSarkWife.h" #import "Sark+Internal.h" // <--- 私有依赖 @implementation PrivateSarkWife - (void)robAllMoneyFromCreditCardOfSark:(Sark *)sark { NSString *password = sark.creditCardPassword; // oh yeah! } @end
2:UIScrollView知识点
- (void)viewDidLoad { [super viewDidLoad]; scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)]; scrollView.backgroundColor = [UIColor redColor]; // 是否支持滑动最顶端 // scrollView.scrollsToTop = NO; scrollView.delegate = self; // 设置内容大小 scrollView.contentSize = CGSizeMake(320, 460*10); // 是否反弹 // scrollView.bounces = NO; // 是否分页 // scrollView.pagingEnabled = YES; // 是否滚动 // scrollView.scrollEnabled = NO; // scrollView.showsHorizontalScrollIndicator = NO; // 设置indicator风格 // scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite; // 设置内容的边缘和Indicators边缘 // scrollView.contentInset = UIEdgeInsetsMake(0, 50, 50, 0); // scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 50, 0, 0); // 提示用户,Indicators flash [scrollView flashScrollIndicators]; // 是否同时运动,lock scrollView.directionalLockEnabled = YES; [self.view addSubview:scrollView]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 200, 320, 40)]; label.backgroundColor = [UIColor yellowColor]; label.text = @"学习scrolleview"; [scrollView addSubview:label]; [label release]; } #pragma mark - /* // 返回一个放大或者缩小的视图 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { } // 开始放大或者缩小 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView: (UIView *)view { } // 缩放结束时 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { } // 视图已经放大或缩小 - (void)scrollViewDidZoom:(UIScrollView *)scrollView { NSLog(@"scrollViewDidScrollToTop"); } */ // 是否支持滑动至顶部 - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { return YES; } // 滑动到顶部时调用该方法 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { NSLog(@"scrollViewDidScrollToTop"); } // scrollView 已经滑动 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { NSLog(@"scrollViewDidScroll"); } // scrollView 开始拖动 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { NSLog(@"scrollViewWillBeginDragging"); } // scrollView 结束拖动 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { NSLog(@"scrollViewDidEndDragging"); } // scrollView 开始减速(以下两个方法注意与以上两个方法加以区别) - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView { NSLog(@"scrollViewWillBeginDecelerating"); } // scrollview 减速停止 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { NSLog(@"scrollViewDidEndDecelerating"); }
3:仿网易滚动菜单及内容视图滚动效果
#import "NewsViewController.h" #import "VideoViewController.h" #import "ReaderViewController.h" #import "ScienceViewController.h" #import "SocietyViewController.h" #import "HotViewController.h" #import "TopLineViewController.h" #import "TitleButton.h" #define ScreenW [UIScreen mainScreen].bounds.size.width #define ScreenH [UIScreen mainScreen].bounds.size.height @interface NewsViewController ()<UIScrollViewDelegate> @property (nonatomic, strong) UIButton *selButton; @property (nonatomic, strong) NSMutableArray *titleBtns; // 标题滚动view @property (weak, nonatomic) IBOutlet UIScrollView *titileScrollView; // 内容滚动view @property (weak, nonatomic) IBOutlet UIScrollView *contentView; @end @implementation NewsViewController - (NSMutableArray *)titleBtns { if (_titleBtns == nil) { _titleBtns = [NSMutableArray array]; } return _titleBtns; } // 头条,热点,视频,社会,订阅,科技(science) - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // 添加所有子控制器 [self setUpChildViewController]; // 设置标题 [self setUpTitle]; // iOS7之后,导航控制器下所有ScrollView都会添加额外滚动区域 self.automaticallyAdjustsScrollViewInsets = NO; // 初始化scrollView [self setUpScrollView]; } - (void)setUpScrollView { self.titileScrollView.showsHorizontalScrollIndicator = NO; self.contentView.contentSize = CGSizeMake(self.childViewControllers.count * [UIScreen mainScreen].bounds.size.width, 0); self.contentView.showsHorizontalScrollIndicator = NO; self.contentView.pagingEnabled = YES; self.contentView.bounces = NO; self.contentView.delegate = self; } - (void)setUpChildViewController { TopLineViewController *topLineVc = [[TopLineViewController alloc] init]; topLineVc.title = @"头条"; [self addChildViewController:topLineVc]; HotViewController *hotVc = [[HotViewController alloc] init]; hotVc.title = @"热点"; [self addChildViewController:hotVc]; VideoViewController *videoVc = [[VideoViewController alloc] init]; videoVc.title = @"视频"; [self addChildViewController:videoVc]; SocietyViewController *societyVc = [[SocietyViewController alloc] init]; societyVc.title = @"社会"; [self addChildViewController:societyVc]; ReaderViewController *readerVc = [[ReaderViewController alloc] init]; readerVc.title = @"订阅"; [self addChildViewController:readerVc]; ScienceViewController *scienceVc = [[ScienceViewController alloc] init]; scienceVc.title = @"科技"; [self addChildViewController:scienceVc]; } // 设置标题 - (void)setUpTitle { NSUInteger count = self.childViewControllers.count; CGFloat btnX = 0; CGFloat btnY = 0; CGFloat btnW = 100; CGFloat btnH = 44; for (NSUInteger i = 0; i < count; i++) { btnX = i * btnW; UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.tag = i; [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; UIViewController *vc = self.childViewControllers[i]; [btn setTitle:vc.title forState:UIControlStateNormal]; [btn setTitleColor:[UIColor redColor] forState:UIControlStateSelected]; btn.frame = CGRectMake(btnX, btnY, btnW, btnH); [btn addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside]; [self.titileScrollView addSubview:btn]; [self.titleBtns addObject:btn]; // 默认选中第一个 if (i == 0) { [self titleClick:btn]; } } self.titileScrollView.contentSize = CGSizeMake(btnW * count, 0); } // 点击标题按钮 - (void)titleClick:(UIButton *)btn { // 0.设置标题按钮居中 [self setUpTitleBtnMiddle:btn]; // 1.滚动到对应的界面 CGFloat offsetX = btn.tag * ScreenW; self.contentView.contentOffset = CGPointMake(offsetX, 0); // 2.添加对应子控制器view到对应的位置 UIViewController *vc = self.childViewControllers[btn.tag]; vc.view.frame = CGRectMake(offsetX, 0, ScreenW, self.contentView.bounds.size.height); // NSLog(@"%@",NSStringFromCGRect(self.contentView.bounds)); [self.contentView addSubview:vc.view]; // 还原之前标题的形变 [self setSelectBtn:btn]; } - (void)setSelectBtn:(UIButton *)btn { _selButton.transform = CGAffineTransformIdentity; [_selButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; _selButton.selected = NO; btn.transform = CGAffineTransformMakeScale(1.3, 1.3); btn.selected = YES; _selButton = btn; } #pragma mark - UIScrollViewDelegate // 减速完成的时候 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { // 获取当前的偏移量 CGFloat offsetX = scrollView.contentOffset.x; // 获取页码 int page = offsetX / scrollView.bounds.size.width; UIButton *btn = self.titileScrollView.subviews[page]; // 选中按钮 [self setSelectBtn:btn]; // 设置按钮居中 [self setUpTitleBtnMiddle:btn]; // 添加对应子控制器到界面上 UIViewController *vc = self.childViewControllers[page]; // 已经加载的view就不需要添加了 if (vc.isViewLoaded) return; vc.view.frame = CGRectMake(offsetX , 0, self.contentView.bounds.size.width, self.contentView.bounds.size.height); [self.contentView addSubview:vc.view]; } // 设置标题按钮居中 - (void)setUpTitleBtnMiddle:(UIButton *)btn { // 计算偏移量 CGFloat offsetX = btn.center.x - ScreenW * 0.5; // 左边偏移多了,表示需要往左边看,可视范围往左边,偏移量就减少,最少应该是0 if (offsetX < 0) offsetX = 0; CGFloat maxOffsetX = self.titileScrollView.contentSize.width - ScreenW; // 右边偏移多了,表示需要往右边看,可视范围往又边,偏移量就增加,最大不超过内容范围 - 屏幕宽度 if (offsetX > maxOffsetX) offsetX = maxOffsetX; [self.titileScrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES]; } // 监听内容view滚动 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat page = scrollView.contentOffset.x / scrollView.bounds.size.width; NSInteger leftIndex = page; // 右边缩放比例 CGFloat rightScale = (page - leftIndex); // 左边缩放比例 CGFloat leftScale = (1 - rightScale); NSInteger rightIndex = leftIndex + 1; // 获取左边按钮 TitleButton *leftBtn = self.titleBtns[leftIndex]; NSInteger count = self.titleBtns.count; // 获取右边按钮 TitleButton *rightBtn; if (rightIndex < count) { rightBtn = self.titleBtns[rightIndex]; } // 设置尺寸 CGFloat leftTransform = leftScale * 0.3 + 1; // 1 ~ 1.3 CGFloat rightTransform = rightScale * 0.3 + 1; // 1 ~ 1.3 leftBtn.transform = CGAffineTransformMakeScale(leftTransform, leftTransform); rightBtn.transform = CGAffineTransformMakeScale(rightTransform, rightTransform); // 设置颜色 /* RGB 红色: 1 0 0 黑色: 0 0 0 */ UIColor *leftColor = [UIColor colorWithRed:leftScale green:0 blue:0 alpha:1]; UIColor *rightColor = [UIColor colorWithRed:rightScale green:0 blue:0 alpha:1]; [leftBtn setTitleColor:leftColor forState:UIControlStateNormal]; [rightBtn setTitleColor:rightColor forState:UIControlStateNormal]; } @end
效果图如下: