1 使用触控实现一个简易的画板
1.1 问题
触控(Touch)是一个UITouch类型的对象,当用户触摸了屏幕上的视图时自动被创建,通常使用触控实现绘图、涂鸦、手写等功能。本案例使用触控实现一个简易的画板,可以在画板上勾画出一条线,如图-1所示:
图-1
1.2 方案
首先在创建好的SingleViewApplication项目中创建一个画板类TRDrawView,继承至UIView,该类有一个NSMutableArray类型的属性points,用于存储手指触摸的轨迹也就是点。
其次在Stroyboard的场景中拖放一个View控件,和屏幕一样大小,然后将View的类型修改为TRDrawView。
然后在TRDrawView类中通过touchesBegan:、touchesMoved:方法获取到手指的触摸点,将点存储到points数组中。
最后在TRDrawView类中重写drawRect方法,该方法中根据手指的触摸轨迹points进行屏幕绘制。切记要在touchesMoved方法中调用setNeedDisplay刷新界面。
1.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建项目和画板类
首先在创建好的SingleViewApplication项目中创建一个画板类TRDrawView,继承至UIView,该类有一个NSMutableArray类型的属性points,用于存储手指触摸的轨迹也就是点,代码如下所示:
- @interface TRDrawView ()
- @property (strong, nonatomic) NSMutableArray *points;
- @end
然后从对象库中拖放一个View控件到Storyboard场景中,View控件的大小和屏幕一样。在右边栏的检查器三将View的类型设置为TRDrawView,如图-2所示:
图-2
步骤二:在TRDawView中获取手指触摸轨迹
在TRDrawView类中首先重写touchesBegan:,在该方法将points属性进行初始化,并获取当前手指的触摸点,存储到points数组中,代码如下所示:
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- {
- self.points = [@[]mutableCopy];
- UITouch *touch = [touches anyObject];
- CGPoint point = [touch locationInView:self];
- NSValue *value = [NSValue valueWithCGPoint:point];
- [self.points addObject:value];
- }
然后再重写touchesMove:方法,在该方法中继续获取手指的当前触摸点,并将触摸点存储到points数组中,代码如下所示:
- - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
- CGPoint point = [touch locationInView:self];
- NSValue *value = [NSValue valueWithCGPoint:point];
- [self.points addObject:value];
- }
步骤三:重写drawRect方法,进行屏幕绘制
在TRDrawView类中重写drawRect方法,该方法中根据手指的触摸轨迹points进行屏幕绘制,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- UIBezierPath *path = [UIBezierPath bezierPath];
- NSValue *value = [self.points firstObject];
- [path moveToPoint:[value CGPointValue]];
- for (NSValue *value in self.points) {
- [path addLineToPoint:[value CGPointValue]];
- }
- path.lineWidth = 4;
- [[UIColor redColor]setStroke];
- [path stroke];
- }
最后要在touchesMoved:和touchesEnded:方法中调用setNeedDisplay刷新界面,代码如下所示:
- - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
- CGPoint point = [touch locationInView:self];
- NSValue *value = [NSValue valueWithCGPoint:point];
- [self.points addObject:value];
- [self setNeedsDisplay];
- }
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- {
- [self setNeedsDisplay];
- }
1.4 完整代码
本案例中,TRDrawView.m文件中的完整代码如下所示:
2 使用纯代码进行界面布局
2.1 问题
纯代码布局就是重写布局方法viewDidLayoutSubviews,在该方法内部计算每个子视图的frame属性。本案例将学习如何使用纯代码进行布局,使界面上的Button和Label控件始终保持在固定的位置,如图-3、图-4所示:
图-3
图-4
2.2 方案
首先创建一个SingleViewApplication项目,将自动布局功能关闭。
在Stroyboard的场景中拖放两个Button控件和一个Label控件,Button放置在屏幕的上方,并且大小一样,Label控件放置在屏幕的右下角。
然后在TRViewController类中重写布局方法viewDidLayoutSubviews,在该方法中根据父视图的bounds计算Button和Label的frame。
2.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建项目,添加控件
首先创建一个SingleViewApplication项目,在右边栏的检查器一中将自动布局功能关闭,如图-5所示:
图-5
在Stroyboard的场景中拖放两个Button控件和一个Label控件,Button放置在屏幕的上方,并且大小一样,Label控件放置在屏幕的右下角,如图-6所示:
图-6
步骤二:重写布局方法viewDidLayoutSubviews,进行界面布局
将Storyboard中的Button控件和Label控件关联成TRViewController的私有属性,代码如下所示:
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet UIButton *button1;
- @property (weak, nonatomic) IBOutlet UIButton *button2;
- @property (weak, nonatomic) IBOutlet UILabel *label;
- @end
在TRViewController类中重写布局方法viewDidLayoutSubviews,在该方法中根据父视图的bounds计算Button和Label的frame,代码如下所示:
- - (void)viewDidLayoutSubviews
- {
- [super viewDidLayoutSubviews];
- CGFloat buttonWidth = (self.view.bounds.size.width - 20 - 20 - 10) * 0.5;
- CGRect frame = CGRectMake(20, self.button1.frame.origin.y, buttonWidth, 40);
- self.button1.frame = frame;
- frame = CGRectMake(self.button1.frame.size.width+30, self.button2.frame.origin.y, buttonWidth, 40);
- self.button2.frame = frame;
- frame = self.label.frame;
- self.label.frame = CGRectMake(self.view.bounds.size.width-20-frame.size.width, self.view.bounds.size.height-20-frame.size.height, frame.size.width, frame.size.height);
- }
2.4 完整代码
本案例中,TRViewController.m文件中的完整代码如下所示:
- #import "TRViewController.h"
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet UIButton *button1;
- @property (weak, nonatomic) IBOutlet UIButton *button2;
- @property (weak, nonatomic) IBOutlet UILabel *label;
- @end
- @implementation TRViewController
- - (void)viewDidLayoutSubviews
- {
- [super viewDidLayoutSubviews];
- CGFloat buttonWidth = (self.view.bounds.size.width - 20 - 20 - 10) * 0.5;
- CGRect frame = CGRectMake(20, self.button1.frame.origin.y, buttonWidth, 40);
- self.button1.frame = frame;
- frame = CGRectMake(self.button1.frame.size.width+30, self.button2.frame.origin.y, buttonWidth, 40);
- self.button2.frame = frame;
- frame = self.label.frame;
- self.label.frame = CGRectMake(self.view.bounds.size.width-20-frame.size.width, self.view.bounds.size.height-20-frame.size.height, frame.size.width, frame.size.height);
- }
- @end
3 根据上边栏和下边栏的高度进行布局
3.1 问题
从iOS7开始,视图控制器会渗透到各种Bar下面,包括:NavigationBar、ToolBar、TabBar、StatusBar等;这些Bar会挤占视图的空间,在布局时就需要根据各种Bar所挤占的空间大小来计算控件的frame,本案例直接在上一个案例的基础上实现,根据上边栏和下边栏的高度对界面进行布局,如图-7、图-8所示:
图-7
图-8
3.2 方案
首先在上一个案例的基础上增加一个NavigationController和TabBarController,在界面的中间拖放一个Button控件,标题设置为“隐藏NavigationBar”,并将Button控件关联成TRViewController的私有方法hideNavigationBar。
然后在TRViewController类中重写布局方法viewDidLayoutSubviews,在该方法中根据父视图的bounds和上下边栏的高度计算Button和Label的frame。
3.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建项目,添加按钮控件
在上一个案例的基础上增加一个NavigationController和TabBarController,在界面的中间拖放一个Button控件,标题设置为“隐藏NavigationBar”,如图-9所示:
图-9
然后将Button控件关联成TRViewController的私有方法hideNavigationBar,该方法的功能是将导航栏隐藏或显示,代码如下所示:
- - (IBAction)hideNavigationBar
- {
- self.navigationController.navigationBarHidden = !self.navigationController.navigationBarHidden;
- }
步骤二:重写布局方法viewDidLayoutSubviews,进行界面布局
在TRViewController类中重写布局方法viewDidLayoutSubviews,在该方法中根据先通过属性self.topLayoutGuide.length和self.bottomLayoutGuide.length获取到上下边栏的高度,然后再通过父视图的bounds和上下边栏的高度计算出Button和Label的frame,代码如下所示:
- - (void)viewDidLayoutSubviews
- {
- [super viewDidLayoutSubviews];
- CGFloat buttonWidth = (self.view.bounds.size.width - 20 - 20 - 10) * 0.5;
- CGFloat top = self.topLayoutGuide.length;
- CGRect frame = CGRectMake(20, top+10, buttonWidth, 40);
- self.button1.frame = frame;
- frame.origin.x += buttonWidth + 10;
- self.button2.frame = frame;
- CGFloat bottom = self.bottomLayoutGuide.length;
- frame = self.label.frame;
- frame = CGRectMake(self.view.bounds.size.width - 20 - frame.size.width , self.view.bounds.size.height - 10 - frame.size.height - bottom, frame.size.width, frame.size.height);
- self.label.frame = frame;
- frame = self.hideButton.frame;
- frame.origin.x = self.view.bounds.size.width * 0.5 - frame.size.width * 0.5;
- frame.origin.y = self.view.bounds.size.height * 0.5 - frame.size.height * 0.5;
- self.hideButton.frame = frame;
- }
3.4 完整代码
本案例中,TRViewController.m文件中的完整代码如下所示:
- #import "TRViewController.h"
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet UIButton *button1;
- @property (weak, nonatomic) IBOutlet UIButton *button2;
- @property (weak, nonatomic) IBOutlet UILabel *label;
- @property (weak, nonatomic) IBOutlet UIButton *hideButton;
- @end
- @implementation TRViewController
- - (IBAction)hideNavigationBar
- {
- self.navigationController.navigationBarHidden = !self.navigationController.navigationBarHidden;
- }
- - (void)viewDidLayoutSubviews
- {
- [super viewDidLayoutSubviews];
- CGFloat buttonWidth = (self.view.bounds.size.width - 20 - 20 - 10) * 0.5;
- CGFloat top = self.topLayoutGuide.length;
- CGRect frame = CGRectMake(20, top+10, buttonWidth, 40);
- self.button1.frame = frame;
- frame.origin.x += buttonWidth + 10;
- self.button2.frame = frame;
- CGFloat bottom = self.bottomLayoutGuide.length;
- frame = self.label.frame;
- frame = CGRectMake(self.view.bounds.size.width - 20 - frame.size.width , self.view.bounds.size.height - 10 - frame.size.height - bottom, frame.size.width, frame.size.height);
- self.label.frame = frame;
- frame = self.hideButton.frame;
- frame.origin.x = self.view.bounds.size.width * 0.5 - frame.size.width * 0.5;
- frame.origin.y = self.view.bounds.size.height * 0.5 - frame.size.height * 0.5;
- self.hideButton.frame = frame;
- }
- @end
4 演示绘制图形的布局
4.1 问题
使用纯代码布局并且AutoLayout关闭的状态下,在drawRect方法中绘制的图形,在视图大小发生变化时图形会失真,本案例学习绘制图形的布局如图-10,图-11所示:
图-10
图-11
4.2 方案
首先在创建好的项目中将自动布局功能关闭,再创建一个TRView类,继承至UIView。
其次在Stroyboard的场景中拖放一个View控件,和屏幕一样大小,然后将View的类型修改为TRView。
然后在TRView类中重写drawRect方法,在屏幕左上方绘制一个三角形。
最后将TRView的contentMode属性设置成Redraw,即可实现绘制的布局,屏幕切换或者变化绘制的图形也不会失真。
4.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRView类,绘制图像
首先在创建好的项目中将自动布局功能关闭,创建一个TRView类,继承至UIView,用于绘制图形。在Storyboard中拖放一个View控件,和屏幕同等大小,并将View的类型修改为TRView,如图-12所示:
图-12
然后将View控件关联成TRViewController的私有属性myView,代码如下所示:
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet TRView *myView;
- @end
最后在TRView类中重写drawRect方法,在屏幕左上方绘制一个三角形,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, 120)];
- [path addLineToPoint:CGPointMake(120, 20)];
- [path closePath];
- path.lineWidth = 4;
- [[UIColor redColor] setStroke];
- [path stroke];
- }
步骤二:进行绘制布局
完成绘制代码,运行程序可见屏幕左上方有一个三角形,但是当切换成横屏时发现三角形失真,如图-13所示:
图-13
解决的办法是,当视图大小发生变化时,进行重新绘制图形,即在布局方法viewDidLayoutSubviews里面调用setNeedDisplay方法即可,代码如下所示:
- - (void)viewDidLayoutSubviews
- {
- [super viewDidLayoutSubviews];
- [self.myView setNeedsDisplay];
- }
但是通常直接将myView的contentMode属性设置为Redraw即可实现绘制布局,相当于调用了上面的代码,将myView的contentMode属性设置为Redraw有两个方法,第一种可以直接通过代码设置,代码如下所示:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.myView.contentMode = UIViewContentModeRedraw;
- }
第二种方法可以直接在Stroyboard中设置,右边栏的检查器四中将Mode选项设置为Redraw即可,如图-14所示:
图-14
将contentMode设置为Redraw之后就不需要再写布局代码,此时切换屏幕绘制图形就不会失真了。
4.4 完整代码
本案例中,TRViewController.m文件中的完整代码如下所示:
- #import "TRViewController.h"
- #import "TRView.h"
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet TRView *myView;
- @end
- @implementation TRViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.myView.contentMode = UIViewContentModeRedraw;
- }
- @end
本案例中,TRView.m文件中的完整代码如下所示:
- #import "TRView.h"
- @implementation TRView
- - (void)drawRect:(CGRect)rect
- {
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, 120)];
- [path addLineToPoint:CGPointMake(120, 20)];
- [path closePath];
- path.lineWidth = 4;
- [[UIColor redColor] setStroke];
- [path stroke];
- }
- @end
5 对乐库项目的播放列表单元格进行布局
5.1 问题
视图自身也可以使用布局方法layoutSubviews对自己的子视图进行布局,本案例使用视图的layoutSubviews方法给乐库项目的播放列表单元格进行布局,如图-15所示:
图-15
5.2 方案
首先创建一个SingleViewApplication项目,将Xcode自带的TRViewController类删除,创建一个TRMusicsTableViewController类,继承至UITableViewController,该类有一个NSArray类型的属性musics用于存储歌曲数据源。
再将Storyboard中自带的场景删除,拖放一个TableViewController到界面中,嵌入一个NavigaitionController。在右边栏的检查器中将TableViewController设置为动态表视图,并和TRMusicsTableViewController类进行绑定。
其次创建一个带有xib的TRMusicCell类,继承至UITableViewCell,在xib文件中进行自定义cell,往cell的contentView视图上拖放所需要的控件。
首先在cell的上方拖放一个Label控件,用于显示歌曲的名字。在cell的下方依次拖放两个ImageView控件和两个Label控件,两个ImageView控件分别用于表示歌曲是否为本地歌曲和是否高清。两个Label控件分别用于显示歌曲的信息和时长。
调整好cell上面各个控件的大小,将个控件关联为TRMusicCell的属性musicNameLabel、albumAndArtistLabel、durationLabel、downloadedImageView以及hdImageView。
然后再创建一个TRMusic类用于存储歌曲的相关信息,该类继承至NSObject,有五个属性,分别为:
NSString类型的name,用于记录歌曲名称;
NSString类型的album,用于记录歌曲所属专辑;
NSString类型的artist,用于记录歌曲的演唱者;
NSString类型的duration,用于记录歌曲的时长;
BOOL类型的highQuality和downloaded,分别用于记录是否高清和是否本地下载。
我们创建一个TRMusicGroup类,用于生成一组模拟的歌曲数据。
最后在TRMusicCell类中定义一个TRMusic类型属性music,用于存储单元格需要展示的歌曲。重写layoutSubviews方法,进行cell的界面布局,该方法中会根据每首歌曲的信息,计算子视图的frame进行布局。
在TRMusicTableViewController类注册Cell,回答三问给表视图加载歌曲数据。
5.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建播放列表项目
首先创建一个SingleViewApplication项目,将Xcode自带的TRViewController类删除,创建一个TRMusicsTableViewController类,继承至UITableViewController,该类有一个NSArray类型的属性musics用于存储歌曲数据源,代码如下所示:
- @interface TRMusicTableViewController : UITableViewController
- @property (strong, nonatomic) NSArray *musics;
- @end
然后将Storyboard中自带的场景删除,拖放一个TableViewController到界面中,嵌入一个NavigaitionController。在右边栏的检查器中将TableViewController设置为动态表视图,并和TRMusicsTableViewController类进行绑定,如图-16所示:
图-16
为了能更灵活的使用自定义cell,由于本案例使用xib的方式进行自定义cell,所以将Storyboard中表视图自带的cell对象删除,如图-17所示:
图-17
步骤二:创建TRMusicCell类,自定义Cell
首先创建一个带有xib的TRMusicCell类,继承至UITableViewCell,在xib文件中进行自定义cell,往cell的contentView视图上拖放所需要的控件。
先在cell的上方拖放一个Label控件,用于显示歌曲的名字。
再在cell的下方依次拖放两个ImageView控件和两个Label控件,两个ImageView控件分别用于表示歌曲是否为本地歌曲和是否高清。两个Label控件分别用于显示歌曲的信息和时长。
设置好cell上面各个控件的大小和属性,如图-18所示:
图-18
最后将cell上的各个控件关联为TRMusicCell的属性musicNameLabel、albumAndArtistLabel、durationLabel、downloadedImageView以及hdImageView,代码如下所示:
- @interface TRMusicCell ()
- @property (weak, nonatomic) IBOutlet UILabel *musicNameLabel;
- @property (weak, nonatomic) IBOutlet UILabel *albumAndArtistLabel;
- @property (weak, nonatomic) IBOutlet UILabel *durationLabel;
- @property (weak, nonatomic) IBOutlet UIImageView *downloadedImageView;
- @property (weak, nonatomic) IBOutlet UIImageView *hdImageView;
- @end
步骤三:创建TRMusic类和歌曲模拟数据
首先创建一个TRMusic类用于存储歌曲的相关信息,该类继承至NSObject,有五个属性,分别为:
NSString类型的name,用于记录歌曲名称;
NSString类型的album,用于记录歌曲所属专辑;
NSString类型的artist,用于记录歌曲的演唱者;
NSString类型的duration,用于记录歌曲的时长;
BOOL类型的highQuality和downloaded,分别用于记录是否高清和是否本地下载。
代码如下所示:
- @interface TRMusic : NSObject
- @property (nonatomic, copy) NSString * name;
- @property (nonatomic, copy) NSString * album;
- @property (nonatomic, copy) NSString * artist;
- @property (nonatomic) NSTimeInterval duration;
- @property (nonatomic) BOOL highQuality;
- @property (nonatomic) BOOL downloaded;
- @end
然后再创建一个TRMusicGroup类,该类提供一个静态方法fakeData,用于生成一组模拟的歌曲数据,代码如下所示:
步骤四:进行自定义cell布局
首先在TRMusicTableViewController类注册Cell,并在TRAppDelegate中对属性musics进行初始化,获取到模拟的歌曲数据,代码如下所示:
- -(BOOL)application:(UIApplication *)application
- didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- NSArray *musicGroups = [TRMusicGroup fakeData];
- TRMusicGroup *group = musicGroups[0];
- UINavigationController *navi = (UINavigationController *)self.window.rootViewController;
- TRMusicTableViewController *musicTVC = (TRMusicTableViewController *) navi.topViewController;
- musicTVC.musics = group.musics;
- return YES;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- [self.tableView registerNib:[UINib nibWithNibName: @"TRMusicCell" bundle:nil] forCellReuseIdentifier:musicCellIdentifier];
- }
然后在TRMusicCell类中定义一个TRMusic类型属性music,用于存储单元格需要展示的歌曲,代码如下所示:
- @interface TRMusicCell : UITableViewCell
- @property (strong, nonatomic) TRMusic *music;
- @end
在TRMusicCell重写layoutSubviews方法,进行cell的界面布局,该方法中会根据每首歌曲的信息,计算子视图的frame进行布局,代码如下所示:
- - (void)layoutSubviews
- {
- [super layoutSubviews];
- CGFloat x = self.downloadedImageView.frame.origin.x;
- if (self.music.downloaded){
- x += 20;
- }
- if (self.music.highQuality) {
- CGRect frame = self.hdImageView.frame;
- frame.origin.x = x;
- self.hdImageView.frame = frame;
- x += 20;
- }
- CGRect frame = self.albumAndArtistLabel.frame;
- frame.origin.x = x;
- self.albumAndArtistLabel.frame = frame;
- }
歌曲的高清和下载图标需要根据歌曲的相关信息进行显示,可以将此部分功能通过重写music的setter方法来实现,代码如下所示:
- - (void)setMusic:(TRMusic *)music
- {
- _music = music;
- self.musicNameLabel.text = music.name;
- self.albumAndArtistLabel.text = [NSString stringWithFormat:@"%@ - %@", music.album, music.artist];
- self.durationLabel.text = [NSString stringWithFormat:@"%d:%02d", (int)music.duration/60, (int)music.duration%60];
- self.downloadedImageView.hidden = !music.downloaded;
- self.hdImageView.hidden = !music.highQuality;
- [self setNeedsLayout];
- }
最后在TRMusicsTableViewController类中回答三问给表视图加载歌曲数据,并将单元格的行高设置为50,代码如下所示:
- -(NSInteger)tableView:(UITableView *)tableView
- numberOfRowsInSection:(NSInteger)section
- {
- return self.musics.count;
- }
- -(UITableViewCell *)tableView:(UITableView *)tableView
- cellForRowAtIndexPath:(NSIndexPath *)indexPath
- {
- TRMusicCell *cell = [tableView dequeueReusableCellWithIdentifier:musicCellIdentifier forIndexPath:indexPath];
- cell.music = self.musics[indexPath.row];
- return cell;
- }
- -(CGFloat)tableView:(UITableView *)tableView
- heightForRowAtIndexPath:(NSIndexPath *)indexPath
- {
- return 50;
- }
5.4 完整代码
本案例中,TRAppDelegate.m文件中的完整代码如下所示:
- #import "TRAppDelegate.h"
- #import "TRMusicGroup.h"
- #import "TRMusicTableViewController.h"
- @implementation TRAppDelegate
- -(BOOL)application:(UIApplication *)application
- didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- NSArray *musicGroups = [TRMusicGroup fakeData];
- TRMusicGroup *group = musicGroups[0];
- UINavigationController *navi = (UINavigationController *)self.window.rootViewController;
- TRMusicTableViewController *musicTVC = (TRMusicTableViewController *) navi.topViewController;
- musicTVC.musics = group.musics;
- return YES;
- }
- @end
本案例中,TRMusicTableViewController.h文件中的完整代码如下所示:
- #import <UIKit/UIKit.h>
- @interface TRMusicTableViewController : UITableViewController
- @property (strong, nonatomic) NSArray *musics;
- @end
本案例中,TRMusicCell.h文件中的完整代码如下所示:
- #import <UIKit/UIKit.h>
- #import "TRMusic.h"
- @interface TRMusicCell : UITableViewCell
- @property (strong, nonatomic) TRMusic *music;
- @end
本案例中,TRMusicCell.m文件中的完整代码如下所示:
- #import "TRMusicCell.h
- @interface TRMusicCell ()
- @property (weak, nonatomic) IBOutlet UILabel *musicNameLabel;
- @property (weak, nonatomic) IBOutlet UILabel *albumAndArtistLabel;
- @property (weak, nonatomic) IBOutlet UILabel *durationLabel;
- @property (weak, nonatomic) IBOutlet UIImageView *downloadedImageView;
- @property (weak, nonatomic) IBOutlet UIImageView *hdImageView;
- @end
- @implementation TRMusicCell
- - (void)setMusic:(TRMusic *)music
- {
- _music = music;
- self.musicNameLabel.text = music.name;
- self.albumAndArtistLabel.text = [NSString stringWithFormat:@"%@ - %@", music.album, music.artist];
- self.durationLabel.text = [NSString stringWithFormat:@"%d:%02d", (int)music.duration/60, (int)music.duration%60];
- self.downloadedImageView.hidden = !music.downloaded;
- self.hdImageView.hidden = !music.highQuality;
- [self setNeedsLayout];//需要重新布局
- }
- //当当前视图的大小发生变化时调用
- - (void)layoutSubviews
- {
- [super layoutSubviews];
- CGFloat x = self.downloadedImageView.frame.origin.x;
- if (self.music.downloaded){
- x += 20;
- }
- if (self.music.highQuality) {
- CGRect frame = self.hdImageView.frame;
- frame.origin.x = x;
- self.hdImageView.frame = frame;
- x += 20;
- }
- CGRect frame = self.albumAndArtistLabel.frame;
- frame.origin.x = x;
- self.albumAndArtistLabel.frame = frame;
- }
- @end
本案例中,TRMusic.h文件中的完整代码如下所示:
- #import <Foundation/Foundation.h>
- @interface TRMusic : NSObject
- @property (nonatomic, copy) NSString * name;
- @property (nonatomic, copy) NSString * album;
- @property (nonatomic, copy) NSString * artist;
- @property (nonatomic) NSTimeInterval duration;
- @property (nonatomic) BOOL highQuality;
- @property (nonatomic) BOOL downloaded;
- @end
本案例中,TRMusicGroup.h文件中的完整代码如下所示:
- #import <Foundation/Foundation.h>
- #import "TRMusic.h"
- typedef NS_ENUM(NSInteger, TRMusicGroupState) {
- TRMusicGroupStateNormal,
- TRMusicGroupStateDownloading,
- TRMusicGroupStateDownloaded
- };
- @interface TRMusicGroup : NSObject
- @property (nonatomic, copy) NSString * name;
- @property (nonatomic, strong) NSArray * musics;
- @property (nonatomic) TRMusicGroupState state;
- + (NSArray *) fakeData;
- @end
本案例中,TRMusicGroup.m文件中的完整代码如下所示:
触控(Touch) 、 布局(Layout)
原文:http://www.cnblogs.com/52190112cn/p/5049447.html