如果這篇文章幫助到了您,希望您能點(diǎn)擊一下喜歡或者評(píng)論,你們的支持是我前進(jìn)的強(qiáng)大動(dòng)力.謝謝!
首先看下我們要制作功能的效果如圖所示:
手勢(shì)解鎖4.gif
第一步:界面搭建
- 在storyboard中的控制器的view中放一張與view相中大小的UIImageView并設(shè)置圖片如效果圖所示,然后再控制器的view中再添加一個(gè)大小合適UIView來(lái)存放9個(gè)按鈕子控件.
-
代碼實(shí)現(xiàn)添加按鈕:創(chuàng)建一個(gè)類(lèi)繼承自UIView,并將這個(gè)類(lèi)和上面storyboard中添加的UIView向關(guān)聯(lián).
Snip20160302_2.png - 界面是一個(gè)九宮格的布局.九宮格實(shí)現(xiàn)思路.(需要一點(diǎn)數(shù)學(xué)思想哈,看的有點(diǎn)模糊的最好畫(huà)圖)
- 先確定有多少列 cloum = 3;
- 計(jì)算出每列之間的距離
- 計(jì)算為: CGFloat margin = (當(dāng)前View的寬度 - 列數(shù) * 按鈕的寬度) / (總列數(shù) + 1)
- 每一列的X的值與它當(dāng)前所在的行有關(guān)
- 當(dāng)前所在的列為:curColum = i % cloum
- 每一行的Y的值與它當(dāng)前所在的行有關(guān).
- 當(dāng)前所在的行為:curRow = i / cloum
- 每一個(gè)按鈕的X值為, margin + 當(dāng)前所在的列 * (按鈕的寬度+ 每個(gè)按鈕之間的間距)
- 每一個(gè)按鈕的Y值為 當(dāng)前所在的行 * (按鈕的寬度 + 每個(gè)按鈕之間的距離)
在創(chuàng)建的UIView類(lèi)中實(shí)現(xiàn)以下代碼:
由于UIView是從storyboard中加載的,所以初始化使會(huì)調(diào)用這個(gè)方法
-(void)awakeFromNib{
初始化
[self setUP];
}
初始化
- (void)setUP{
for (int i = 0; i < 9; i++) {
添加按鈕
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
設(shè)置圖片
[btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
設(shè)置選中狀態(tài)的下圖片
[btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
添加按鈕
[self addSubview:btn];
}
}
布局子控件
- (void)layoutSubviews{
[super layoutSubviews];
總列數(shù)
int cloumn = 3;
按鈕高寬
CGFloat btnWH = 74;
每列之間的間距
CGFloat margin = (self.bounds.size.width - cloumn * btnWH) / (cloumn + 1);
當(dāng)前所在的列
int curClounm = 0;
當(dāng)前所在的行
int curRow = 0;
CGFloat x = 0;
CGFloat y = 0;
取出所有的控件
for (int i = 0; i < self.subviews.count; i++) {
計(jì)算當(dāng)前所在的列
curClounm = i % cloumn;
計(jì)算當(dāng)前所在的行.
curRow = i / cloumn;
計(jì)算Y
x = margin + (margin + btnWH) * curClounm;
計(jì)算Y.
y = (margin +btnWH) * curRow;
UIButton *btn = self.subviews[i];
btn.frame = CGRectMake(x, y, btnWH, btnWH);
}
}
第二步:設(shè)置按鈕選中的狀態(tài)
Snip20160302_7.png
/**
* 獲取當(dāng)前手指所在的點(diǎn)
*
* @param touches touches集合
*
* @return 當(dāng)前手指所在的點(diǎn).
*/
- (CGPoint)getCurrentPoint:(NSSet *)touches{
UITouch *touch = [touches anyObject];
return [touch locationInView:self];
}
/**
* 判斷一個(gè)點(diǎn)在不在按鈕上.
*
* @param point 當(dāng)前點(diǎn)
*
* @return 如果在按鈕上, 返回當(dāng)前按鈕, 如果不在返回nil.
*/
- (UIButton *)btnRectContainsPoint:(CGPoint)point{
遍歷所有的子控件
for (UIButton *btn in self.subviews) {
判斷手指當(dāng)前點(diǎn)在不在按鈕上.
if (CGRectContainsPoint(btn.frame, point)) {
在按鈕上.返回當(dāng)前按鈕
return btn;
}
}
return nil;
}
手指點(diǎn)擊時(shí)讓按鈕成選中狀態(tài)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
判斷當(dāng)前手指在不在按鈕上,如果在按鈕上, 讓按鈕成為選中狀態(tài).
(我將下面1,2兩個(gè)方法按功能模塊單獨(dú)抽取出來(lái).)
1.獲取當(dāng)前手指所在的點(diǎn)
CGPoint curP = [self getCurrentPoint:touches];
2.判斷當(dāng)前手指所在的點(diǎn)在不在按鈕上.
UIButton *btn = [self btnRectContainsPoint:curP];
if (btn) {
btn.selected = YES;
}
}
手指移動(dòng)時(shí),按鈕選中,連線(xiàn)到當(dāng)前選中的按鈕
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
判斷當(dāng)前手指在不在按鈕上,如果在按鈕上, 讓按鈕成為選中狀態(tài).
1.獲取當(dāng)前手指所在的點(diǎn)
CGPoint curP = [self getCurrentPoint:touches];
2.判斷當(dāng)前手指所在的點(diǎn)在不在按鈕上.
UIButton *btn = [self btnRectContainsPoint:curP];
if (btn) {
btn.selected = YES;
}
}
第三步:連線(xiàn)
@interface ClockView()
/**
* 選中的按鈕數(shù)組.
* 每次選中一個(gè)按鈕時(shí),都把按鈕添加到數(shù)組當(dāng)中.移動(dòng)添加到按鈕當(dāng)中時(shí)做一次重繪.
* 重繪過(guò)程中取出所有保存的按鈕, 判斷是不是第一個(gè)按鈕, 如果是第一個(gè)按鈕,那就讓它成為路徑的起點(diǎn).
* 如果不是第一個(gè)按鈕,那就添加一根線(xiàn)到按鈕的中心點(diǎn).
*/
@property(nonatomic,strong)NSMutableArray *selectBtn;
@end
懶加載數(shù)組.
-(NSMutableArray *)selectBtn{
if (_selectBtn == nil) {
_selectBtn = [NSMutableArray array];
}
return _selectBtn;
}
手指點(diǎn)擊時(shí)讓按鈕成選中狀態(tài)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
判斷當(dāng)前手指在不在按鈕上,如果在按鈕上, 讓按鈕成為選中狀態(tài).
1.獲取當(dāng)前手指所在的點(diǎn)
CGPoint curP = [self getCurrentPoint:touches];
2.判斷當(dāng)前手指所在的點(diǎn)在不在按鈕上.
UIButton *btn = [self btnRectContainsPoint:curP];
if (btn && btn.selected == NO) {如果按鈕已經(jīng)是選中狀態(tài),就不讓它再添加到數(shù)組當(dāng)中
讓按鈕成為選中狀態(tài)
btn.selected = YES;
把選中按鈕添加到數(shù)組當(dāng)中
[self.selectBtn addObject:btn];
}
}
手指移動(dòng)時(shí),按鈕選中,連線(xiàn)到當(dāng)前選中的按鈕
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
判斷當(dāng)前手指在不在按鈕上,如果在按鈕上, 讓按鈕成為選中狀態(tài).
1.獲取當(dāng)前手指所在的點(diǎn)
CGPoint curP = [self getCurrentPoint:touches];
2.判斷當(dāng)前手指所在的點(diǎn)在不在按鈕上.
UIButton *btn = [self btnRectContainsPoint:curP];
if (btn && btn.selected == NO) {//如果按鈕已經(jīng)是選中狀態(tài),就不讓它再添加到數(shù)組當(dāng)中
讓按鈕成為選中狀態(tài)
btn.selected = YES;
把選中按鈕添加到數(shù)組當(dāng)中
[self.selectBtn addObject:btn];
}
每次手指移動(dòng)時(shí)做一次重繪.
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
創(chuàng)建路徑.
UIBezierPath *path = [UIBezierPath bezierPath];
取出所有保存的選中按鈕連線(xiàn).
for(int i = 0; i < self.selectBtn.count;i++){
UIButton *btn = self.selectBtn[i];
判斷當(dāng)前按鈕是不是第一個(gè),如果是第一個(gè),把它的中心設(shè)置為路徑的起點(diǎn).
if(i == 0){
設(shè)置起點(diǎn).
[path moveToPoint:btn.center];
}else{
添加一根線(xiàn)到當(dāng)前按鈕的圓心.
[path addLineToPoint:btn.center];
}
}
設(shè)置顏色
[[UIColor redColor] set];
設(shè)置線(xiàn)寬
[path setLineWidth:10];
設(shè)置線(xiàn)的連接樣式
[path setLineJoinStyle:kCGLineJoinRound];
繪制路徑.
[path stroke];
}
第四步:最后的業(yè)務(wù)邏輯
- 實(shí)現(xiàn)以上功能后雖然能實(shí)現(xiàn)連線(xiàn)的功能,但是連線(xiàn)只能在按鈕之間,按鈕與手指之間并不能實(shí)現(xiàn)連線(xiàn).下面就來(lái)處理這個(gè)問(wèn)題并實(shí)現(xiàn)一些收尾的工作
@interface ClockView()
/**
* 選中的按鈕數(shù)組.
*/
@property(nonatomic,strong)NSMutableArray *selectBtn;
/**
* 當(dāng)前手指移動(dòng)的點(diǎn)
* 記錄當(dāng)前手指的點(diǎn),數(shù)組當(dāng)中所有的點(diǎn)都繪制完畢后, 再添加一根線(xiàn)到當(dāng)前手指所在的點(diǎn).
*/
@property(nonatomic,assign)CGPoint curP;
@end
手指松開(kāi)時(shí),按鈕取消選中狀態(tài),清空所有的連線(xiàn).
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
1.取消所有選中的按鈕,查看選中按鈕的順序(根據(jù)創(chuàng)建按鈕時(shí)綁定的tag值)
NSMutableString *str = [NSMutableString string];
for (UIButton *btn in self.selectBtn) {
[str appendFormat:@"%ld",btn.tag];
btn.selected = NO;
}
2.清空所有的連線(xiàn).
[self.selectBtn removeAllObjects];
3.重繪
[self setNeedsDisplay];
NSLog(@"選中按鈕順序?yàn)?%@",str);
}
- (void)drawRect:(CGRect)rect {
如果數(shù)組當(dāng)中沒(méi)有元素,就不讓它進(jìn)行繪圖.直接返回.
if(self.selectBtn.count <= 0) return;
創(chuàng)建路徑.
UIBezierPath *path = [UIBezierPath bezierPath];
取出所有保存的選中按鈕連線(xiàn).
for(int i = 0; i < self.selectBtn.count;i++){
UIButton *btn = self.selectBtn[i];
判斷當(dāng)前按鈕是不是第一個(gè),如果是第一個(gè),把它的中心設(shè)置為路徑的起點(diǎn).
if(i == 0){
設(shè)置起點(diǎn).
[path moveToPoint:btn.center];
}else{
添加一根線(xiàn)到當(dāng)前按鈕的圓心.
[path addLineToPoint:btn.center];
}
}
連完先中的按鈕后, 在選中按鈕之后,添加一根線(xiàn)到當(dāng)前手指所在的點(diǎn).
[path addLineToPoint:self.curP];
設(shè)置顏色
[[UIColor redColor] set];
設(shè)置線(xiàn)寬
[path setLineWidth:10];
設(shè)置線(xiàn)的連接樣式
[path setLineJoinStyle:kCGLineJoinRound];
繪制路徑.
[path stroke];
}
Demo已上傳
地址:http://git.oschina.net/li_xiao_nan/overhand
寫(xiě)的不好的話(huà)忘大家指出,一起進(jìn)步.謝謝!