写在前头,最近看cocoaChina上面整理出了很多有名的博客,自己做iOS两年了,也想好好做出点东西来而不是每天只知道挣那点工资,所以买了服务器新建博客了,拿以前仿写的组件来练习写写博客。这个组件是有一天晚上睡前听QQ音乐做定时关闭时看见的,觉得效果不错而且能用上的地方也应该不少,所以萌生了仿写一个的想法,第二天便开始动手。文章尽量都是跟大家一起交流分析思路,我觉得仿东西最重要的是观察和分析,看代码时间也有好久了,我也重新看了下代码才开始写的,如有写错的地方望见谅~~
Jasper(ZR)原创文章,转载请注明出处。
首先分析功能和效果,以此为入手点:
- slider可以通过点击目标点来设置时间。
- 可以通过拖拽来设置,拖拽手松后自动释放到最近的点。
- 在滑动的过程中会对经过的点做提高文字和加粗文字的动画效果。
再分析UI组成:
- 有正常的横线和节点以及touchPad组成。
- 有标题,标题的个数可以自定义。
先做头文件的定义及Slider的构造函数定义,构造函数应包含frame和title(数组),这样就确定了构造返回来的Slider的具体大小,位置和各个节点的标题。delegate函数暂时只写一个返回当前选择index的函数,因为是初次实现功能的仿写,先不做更完善的重构。属性应设置有横线view、横线粗细度、第一节点和最后节点距边缘的间距、标题数组、touchPad按钮、当前选择的index。综上分析,头文件代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
// // pointSlider.h // Test // // Created by Jasper on 14-9-28. // Copyright (c) 2014年 Jasper. All rights reserved. // /** * @Author 14-09-29 17:09:45 * * @brief 此版仅是为了实现功能,尽可能优化了一下,但代码还是有重复,ARC环境,也许会存在内存泄露。本来以为我的测试项目是ARC,结果写delegate的时候用weak发现报错,才发现这个测试项目是MRC,ARC的童鞋自行改一下weak吧~~~还麻烦写的不好勿喷。。。学习思路就好,尽可能详细注释给大家了 * * @return 仿写QQ音乐定时关闭的slider */ #import <UIKit/UIKit.h> @protocol PointSliderDelegate <NSObject> @optional - (void)pointSelected:(int)index; @end @interface PointSlider : UIView { //线宽 CGFloat lineWidth; //间距 CGFloat padding; //放点的数组 NSMutableArray *pointArray; //放title的数组 NSMutableArray *titleArray; //横线 UIView *line; //按钮 UIImageView *touchPad; //当前选中点的index int currPoint; } @property(nonatomic, assign) id <PointSliderDelegate> delegate; /** * @Author 14-09-29 16:09:21 * * @brief 用title数组构造 * * @param frame 当前控件的frame * @param titleArray 要创建的title数组集合 * * @return 返回构造完成的对象自身 */ - (id)initWithFrame:(CGRect)frame andTitle:(NSArray *)_titleArray; @end |
再写.m具体实现,这里横线采用UIView,touchPad采用UIImageView,目的是view可以自定义粗细和背景颜色,这里把view做成1像素高,把背景颜色当做是横线的颜色,这样横线就做成了。touchPad采用imageView的原因是如果不设置图片可以直接设置背景色和阴影也能做出有扁平的效果,如果有美术的出图,也可以更方便的设置上去。构造函数代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
- (id)initWithFrame:(CGRect)frame andTitle:(NSArray *)_titleArray { self = [super initWithFrame:frame]; if (self) { //横线宽 lineWidth = 1; //左右间距 padding = 20; line = [[UIView alloc] init]; line.frame = CGRectMake(padding, (frame.size.height - lineWidth)/2 + 5, frame.size.width - padding * 2, lineWidth); line.backgroundColor = [UIColor colorWithRed:0.423 green:0.410 blue:0.476 alpha:1.000]; [self addSubview:line]; for (int i = 0; i < [_titleArray count]; i++) { [self addPoint:[_titleArray objectAtIndex:i]]; } touchPad = [[UIImageView alloc] init]; touchPad.frame = CGRectMake(0, 0, 20, 20); touchPad.center = CGPointMake(padding, line.center.y); touchPad.layer.cornerRadius = touchPad.frame.size.width / 2; touchPad.backgroundColor = [UIColor colorWithRed:0.455 green:0.866 blue:0.978 alpha:1.000]; touchPad.userInteractionEnabled = YES; //绘制阴影 touchPad.layer.shadowOffset = CGSizeMake(1.5, 2); touchPad.layer.shadowColor = [UIColor colorWithRed:0.396 green:0.372 blue:0.353 alpha:1.000].CGColor; touchPad.layer.shadowRadius = 2; touchPad.layer.shadowOpacity = 0.8; [self addSubview:touchPad]; [self resetPointFrame]; } return self; } |
构造函数里面主要根据传入的frame和标题设置横线的大小和节点标题,其中把间距和线宽作为全局属性是因为后面也常用到这两个变量。再更完善的时候其实也可以把线的颜色和节点颜色等等这些Style上的属性单独提出来,方便复用的时候自定义更多的样式。
这里的思路是先add一个横线view,之后遍历节点标题数组,把每个节点标题取出来构造小节点view放到横线上,并且add到节点数组中,然后对应构造UILabel设置标题,放到节点正上方,也add到标题数组中。这里仅仅只是设置节点跟标题的Y坐标,保证高度对了就行,待最后添加完全部的节点和标题之后,调用一次resetPoint函数来计算修改每个节点的X坐标,在resetPoint中再根据当前的point调用resetTitle来计算当前节点的标题X坐标。
有人要问为什么这里不每add一个节点就即使计算X坐标,这里1是因为初次仅为实现功能,代码设计的时候没考虑那么周到和完整,很多也都是边写边修正的,2是这个resetPoint函数在其之后的滑动动画中也离不开,所以就复用这个函数提高复用度了。
至此UI元素上已经都完全满足最开始分析出来的需求,一个不能滑动的,静止的Slider已经制作出来了,下一步是完成功能上的需求,主要分为滑动、判定选中节点、动画。滑动和点击使用touch事件来写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) { //点击按钮外的某一点 [self dealTouch:touches]; }else { //拖动按钮 [self dealMove:touches]; } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) return ; [self dealMove:touches]; [self setMoveTitleAnimation]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) return ; [self computingCurrPoint]; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) return ; [self computingCurrPoint]; } |
这里touch事件的思路是这样的,分别实现点击开始、拖动、结束、取消这4个函数,Slider的操作模式有两种,1是点击目标节点,2是滑动到目标节点。这里是1还是2通过touchView来判断,如果点击开始的时候touchView是之前定义的touchPad,那么就说明是开始滑动,即使是点了一下touchPad,也算是滑动(因为不是点在节点上),执行滑动处理函数,具体滑动怎么处理一会再讲;如果touchView不是touchPad,那么就说明是点击节点操作,这时候应该执行点击处理函数,把touchPad移动到选中的节点上。
那么滑动处理和点击处理是怎么做的呢,下面就来讲解这两块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
/** * @Author 14-09-29 16:09:38 * * @brief 处理点击事件 * * @param touches */ - (void)dealTouch:(NSSet *)touches { UITouch *touch = [touches anyObject]; //如果拖动按钮,当前点击处坐标 CGPoint localPoint = [touch locationInView:self]; //统计点的个数 int pointSum = [pointArray count]; //计算除去两边间距之后,每两个点之间线长度 int lineLength = (self.frame.size.width - padding * 2) / (pointSum - 1); //计算除去间距,点击出距离线起点的长度,也就是点击的跨度 int touchLength = localPoint.x - padding; //通过点击跨度,除以线长度,得到点击跨过了几个点 int touchPointIndex = (touchLength / lineLength); //计算出抛去跨过点的整个长度后余下的长度 int overLength = localPoint.x - padding - touchPointIndex * lineLength; //如果余下的长度大于当前跨过点和下一点的中点又三分之一,则当前选中点等于跨过点加1,也就是下一个点(除以3是为了限制点击点周围的三分之一范围内才识别点击事件) if(overLength > lineLength / 2 + (lineLength /3)) { currPoint = touchPointIndex + 1; }else if((overLength < lineLength / 2 - (lineLength /3))) { //如果没大于中点,则当前选中点就是当前的跨过点 currPoint = touchPointIndex; } //通过计算得到的当前选中点,计算出按钮应该在的中心点坐标,并动画赋值 [UIView animateWithDuration:0.4 animations:^{ touchPad.center = CGPointMake(padding + currPoint * lineLength, touchPad.center.y); //设置title的位置 [self resetTitleFrame]; } completion:^(BOOL finished) { if ([self.delegate respondsToSelector:@selector(pointSelected:)]) { [self.delegate pointSelected:currPoint]; } }]; } /** * @Author 14-09-29 16:09:09 * * @brief 处理拖动事件 * * @param touches */ - (void)dealMove:(NSSet *)touches { UITouch *touch = [touches anyObject]; CGPoint localPoint = [touch locationInView:self]; //计算X偏移量 CGFloat offsetX = localPoint.x - touchPad.center.x; //通过偏移量计算新的中心点X坐标值 CGFloat newCenterX = touchPad.center.x + offsetX; //限制新中心点X的范围为线的左右两端之内 newCenterX = MIN(self.frame.size.width - padding, newCenterX); newCenterX = MAX(padding, newCenterX); //一下三行为根据计算得来的新中心点X坐标创建新的按钮中心点,并赋值,因为本身在拖动过程中,所以不需要动画 CGPoint newCenter; newCenter = CGPointMake(newCenterX, touchPad.center.y); touchPad.center = newCenter; } |
计算因为横线上的节点是用UIView去做的,仅仅是显示出节点的作用,并不具备点击的功能,也没考虑去用btn做,觉得麻烦。所以这里点击目标节点是通过touch点击的距离来判定的,具体思路为,touch相对于横线的X坐标再减去左间距,就是点击处相对于第一个节点的距离,我这里把他叫做跨度,然后用它去除以每个节点间线的长度,就得到了当前点击的X坐标越过了几个节点。假设先把他当做当前选中的节点。因为跨度不可能正好等于节点的X坐标,都会或多或少些,所以我们用 跨度 – 节点数 * 节点间距 = 剩余长度 来得到跨度的余数,节点是有一个判定范围的,这个范围我定义成节点间距的一半又三分之一,也就是说余数大于节点间距的一半又多了长度的三分之一,那就说明跨过了当前选中点而且更接近下一个点,那么真正的选中点就应该是之前假设的跨过整数点 + 1,如果余数比节点间距一半减去三分之一,那就是真正的选中点更接近于当前假设的选中点,则假设选中点无需+1,最后,有了当前的选中点,就可以resetTitle,主要是设置frame坐标和是否加粗。PS:这里面现在写博客的时候感觉当时写麻烦了,也是因为第一次为了实现功能,很多都是边写边改的,之后一直没有重构完善,现在看来判定可以写的更简单些,设定一个节点判定范围,比如余数 大于 整个节点间距 – 判定范围,就是更接近下一个点,如果余数 小于 判定范围,就是更接近假设点。
上一段讲了怎么做点击节点的处理,这一段讲滑动处理,在touchMove里面主要有两个执行函数,1是拖动touchPad,主要是通过计算center和max、min函数限制拖动范围来实现拖动效果,2是执行setMoveTitleAnimation来在滑动过程中动画的改变标题效果。拖动touchPad没什么好说的,我代码尽可能详细注释了,这里主要说说怎么动画改变标题效果,其实这块当初没太考虑效率,我做东西一般两遍,第一次追求快速实现功能,第二次优化重构,技术有限啦,所以目前是这么个套路。动画改变标题其实是touchMove的循环调用,每次都计算判定,跟上一段讲的跨度原理一样,每move一次都执行一次,然后给当前选中的节点标题改变样式。
最后,在应该的地方做delegate的调用和动画的调用,大体就完成了。详细.m文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
// // pointSlider.m // Test // // Created by Jasper on 14-9-28. // Copyright (c) 2014年 Jasper. All rights reserved. // #import "PointSlider.h" #import <QuartzCore/QuartzCore.h> @implementation PointSlider - (id)initWithFrame:(CGRect)frame andTitle:(NSArray *)_titleArray { self = [super initWithFrame:frame]; if (self) { //横线宽 lineWidth = 1; //左右间距 padding = 20; line = [[UIView alloc] init]; line.frame = CGRectMake(padding, (frame.size.height - lineWidth)/2 + 5, frame.size.width - padding * 2, lineWidth); line.backgroundColor = [UIColor colorWithRed:0.423 green:0.410 blue:0.476 alpha:1.000]; [self addSubview:line]; for (int i = 0; i < [_titleArray count]; i++) { [self addPoint:[_titleArray objectAtIndex:i]]; } touchPad = [[UIImageView alloc] init]; touchPad.frame = CGRectMake(0, 0, 20, 20); touchPad.center = CGPointMake(padding, line.center.y); touchPad.layer.cornerRadius = touchPad.frame.size.width / 2; touchPad.backgroundColor = [UIColor colorWithRed:0.455 green:0.866 blue:0.978 alpha:1.000]; touchPad.userInteractionEnabled = YES; //绘制阴影 touchPad.layer.shadowOffset = CGSizeMake(1.5, 2); touchPad.layer.shadowColor = [UIColor colorWithRed:0.396 green:0.372 blue:0.353 alpha:1.000].CGColor; touchPad.layer.shadowRadius = 2; touchPad.layer.shadowOpacity = 0.8; [self addSubview:touchPad]; [self resetPointFrame]; } return self; } /** * @Author 14-09-29 16:09:29 * * @brief 对线添加点 * * @param title 添加点的标题 */ - (void)addPoint:(NSString *)title { if (!pointArray) { pointArray = [[NSMutableArray alloc] init]; } if (!titleArray) { titleArray = [[NSMutableArray alloc] init]; } UIView *_point = [[UIView alloc] init]; _point.frame = CGRectMake(0, 0, lineWidth * 5, lineWidth * 5); _point.backgroundColor = [UIColor colorWithRed:0.199 green:0.739 blue:1.000 alpha:1.000]; //绘制圆点 _point.layer.cornerRadius = lineWidth * 2.5; [self addSubview:_point]; [pointArray addObject:_point]; UILabel *_title = [[UILabel alloc] init]; _title.frame = CGRectMake(0, 0, 30, 20); _title.textAlignment = NSTextAlignmentCenter; _title.text = title; [self addSubview:_title]; [titleArray addObject:_title]; } /** * @Author 14-09-29 16:09:40 * * @brief 根据添加的点的个数,重置每个点的位置和title的位置 */ - (void)resetPointFrame { int pointSum = [pointArray count]; for (int i = 0; i < [pointArray count]; i++) { UIView *_point = [pointArray objectAtIndex:i]; if (0 == i) { //首节点 _point.center = CGPointMake(padding, line.center.y); }else if (pointSum - 1 == i) { //最后一个节点 _point.center = CGPointMake(self.frame.size.width - padding, line.center.y); }else { //其余的节点 _point.center = CGPointMake((padding + (self.frame.size.width - padding * 2) / (pointSum - 1) * i), line.center.y); } [self resetTitleFrame]; } } /** * @Author 14-09-29 16:09:38 * * @brief 处理点击事件 * * @param touches */ - (void)dealTouch:(NSSet *)touches { UITouch *touch = [touches anyObject]; //如果拖动按钮,当前点击处坐标 CGPoint localPoint = [touch locationInView:self]; //统计点的个数 int pointSum = [pointArray count]; //计算除去两边间距之后,每两个点之间线长度 int lineLength = (self.frame.size.width - padding * 2) / (pointSum - 1); //计算除去间距,点击出距离线起点的长度,也就是点击的跨度 int touchLength = localPoint.x - padding; //通过点击跨度,除以线长度,得到点击跨过了几个点 int touchPointIndex = (touchLength / lineLength); //计算出抛去跨过点的整个长度后余下的长度 int overLength = localPoint.x - padding - touchPointIndex * lineLength; //如果余下的长度大于当前跨过点和下一点的中点又三分之一,则当前选中点等于跨过点加1,也就是下一个点(除以3是为了限制点击点周围的三分之一范围内才识别点击事件) if(overLength > lineLength / 2 + (lineLength /3)) { currPoint = touchPointIndex + 1; }else if((overLength < lineLength / 2 - (lineLength /3))) { //如果没大于中点,则当前选中点就是当前的跨过点 currPoint = touchPointIndex; } //通过计算得到的当前选中点,计算出按钮应该在的中心点坐标,并动画赋值 [UIView animateWithDuration:0.4 animations:^{ touchPad.center = CGPointMake(padding + currPoint * lineLength, touchPad.center.y); //设置title的位置 [self resetTitleFrame]; } completion:^(BOOL finished) { if ([self.delegate respondsToSelector:@selector(pointSelected:)]) { [self.delegate pointSelected:currPoint]; } }]; } /** * @Author 14-09-29 16:09:09 * * @brief 处理拖动事件 * * @param touches */ - (void)dealMove:(NSSet *)touches { UITouch *touch = [touches anyObject]; CGPoint localPoint = [touch locationInView:self]; //计算X偏移量 CGFloat offsetX = localPoint.x - touchPad.center.x; //通过偏移量计算新的中心点X坐标值 CGFloat newCenterX = touchPad.center.x + offsetX; //限制新中心点X的范围为线的左右两端之内 newCenterX = MIN(self.frame.size.width - padding, newCenterX); newCenterX = MAX(padding, newCenterX); //一下三行为根据计算得来的新中心点X坐标创建新的按钮中心点,并赋值,因为本身在拖动过程中,所以不需要动画 CGPoint newCenter; newCenter = CGPointMake(newCenterX, touchPad.center.y); touchPad.center = newCenter; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; // NSLog(@"touchesBegan"); if(touchView != touchPad) { //点击按钮外的某一点 [self dealTouch:touches]; }else { //拖动按钮 [self dealMove:touches]; } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) return ; // NSLog(@"touchesMoved"); [self dealMove:touches]; [self setMoveTitleAnimation]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) return ; // NSLog(@"touchesEnded"); [self computingCurrPoint]; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIView *touchView = [touch view]; if(touchView != touchPad) return ; // NSLog(@"touchesCancelled"); [self computingCurrPoint]; } /** * @Author 14-09-29 16:09:43 * * @brief 拖动结束时计算当前选中点的index,并动画选中 */ - (void)computingCurrPoint { int pointSum = [pointArray count]; int lineLength = ((self.frame.size.width - padding * 2) / (pointSum - 1)) / 1; int touchLength = touchPad.center.x - padding; int touchPointIndex = (touchLength / lineLength); int overLength = touchPad.center.x - padding - touchPointIndex * lineLength; if(overLength > lineLength / 2) { currPoint = touchPointIndex + 1; }else { currPoint = touchPointIndex; } [UIView animateWithDuration:0.4 animations:^{ touchPad.center = CGPointMake(padding + currPoint * lineLength, touchPad.center.y); [self resetTitleFrame]; } completion:^(BOOL finished) { if ([self.delegate respondsToSelector:@selector(pointSelected:)]) { [self.delegate pointSelected:currPoint]; } }]; } /** * @Author 14-09-29 16:09:31 * * @brief 设置拖动过程中title动画,主要是改变title的位置,跟处理点击事件原理类似,计算出当前选中的点,根据选中点遍历设置title位置 */ - (void)setMoveTitleAnimation { int pointSum = [pointArray count]; int lineLength = ((self.frame.size.width - padding * 2) / (pointSum - 1)) / 1; int touchLength = touchPad.center.x - padding; int touchPointIndex = (touchLength / lineLength); int overLength = touchPad.center.x - padding - touchPointIndex * lineLength; if(overLength > lineLength / 2) { currPoint = touchPointIndex + 1; }else { currPoint = touchPointIndex; } [UIView animateWithDuration:0.4 animations:^{ [self resetTitleFrame]; } completion:^(BOOL finished) { }]; } /** * @Author 14-09-29 16:09:14 * * @brief 根据当前选中点设置title的位置和字体 */ - (void)resetTitleFrame { for (int i = 0; i < [titleArray count]; i++) { UIView *_point = [pointArray objectAtIndex:i]; UILabel *_title = [titleArray objectAtIndex:i]; //循环遍历title,选中的点对应title加粗字体,位置上移 if (currPoint == i) { _title.center = CGPointMake(_point.center.x, _point.center.y - 20); _title.font = [UIFont fontWithName:@"Helvetica-Bold" size:9]; }else { _title.center = CGPointMake(_point.center.x, _point.center.y - 15); _title.font = [UIFont systemFontOfSize:9]; } } } @end |
写在最后,因为是第一个练手文章,掌握不好讲解的详细程度,讲个少了怕看不懂,讲的多了以一个Slider而言有点还觉得过于详细。代码写的也不是特别好,大家见谅,更重要的是想跟大家交流分析思路的方法~~~这个博文写都有点文字性多,如有不懂的地方希望大家画下图就理解了。
写的很不错,虽然看不懂,想知道,正常情况下也是文件写好后再一个个加文件和函数注释么?
这个看正常的习惯把~~一般是边开发边注释的,比如写完函数名,就注释,写明函数功能和参数,也有写完发现不后期代码修改,再修改注释的~~~
评论板块本来是搭配多说评论系统用的,评论列表的部分已经在想怎么写了。其实可以设置顶部的头像Logo的。
嗯嗯,明天我看看设置个顶部的Logo,你自己博客现在用的主题,右边的博文部分,和评论部分,都很好啦~~~Yarn和新主题我也很乐意帮你测试的说~~抱歉这么晚给你评论,不知道发送的邮件会不会打扰你
同学,我也叫jasper
嘿嘿,你也是做iOS的嘛~?
Make a more new posts please 🙂
___
Sanny
代码咋全居左显示了