0%

背景

昨天发布的博客 港版苹果充电器DIY全过程 里使用了太多的图片,看了下好几十兆大,这怎么能行呢,作死的节奏啊!所以找了一款 mac 无损图片压缩工具 – ImageOptim ,压缩比特别高,支持 PNG/JPEG/GIF 等主流格式,并且是免费的!

  • 可以直接拖入文件夹批量处理,也可以直接拖入图片,默认自动开始处理;亲身感受如下, 这是我昨天博客的图片:


  • 技巧:这里可以调节压缩比,默认的压缩比小些,博客图片质量不用很高,可以压缩定得更小些:

  • 这是今天的截图,先压缩吧:

有了这个工具就可以大大减小图片的体积了,并且是无损的,图片质量不受影响!

中秋节到了,先说声:“中秋接快乐” !
昨天晚上手机没电了,怎么办?这还用问!?脑瘫么?哦,不是啦;木有充电器啊,那就用电脑充吧,土豪么,吊!之前是用我的pro充,这次用公司发的电脑吧,两年不用忘记了windows睡眠了,结果第二天手机还是不到百分之三十的电量!这就是没有充电器的不便啊!我有港版的充电器啊,可是大陆的插排不能用啊,我有个插排,可以插上,可是不通电啊,仔细观察了下是因为港版的只有金属头是导电的,这个待会看图就明白啦!作为一个geek当然不甘寂寞!

注意事项:需要给电容放电,虽然电容存储的电量不多,但是电压不低呢,我亲身感受过,你会被电的跳起来。。。

放电方法很简单,用改锥短路电容两极就行啦,如果你看到了火花,那就说明电容里确实存了电!最后一张图就是印证哦,电容存储的电供LED亮了15s呢!

(有史以来图片最多的一次了,一共41张,wifi下浏览吧,土豪就当我没说吧…)

  • 还没开始动工呢,已经被我深深地伤害了呀…..

  • 再看看吧:

  • 先把这层模揭掉:


  • 接着就用我为数不多的工具开始割肉啦,埋头苦干了好一会,没啥反应啊!这苹果的充电器没螺丝,都是胶粘好的,用刀子划不开啊!怎么办,记得上次查过融化公交卡的办法,有种化学药剂天那水就可以,网上开始查,发现这货好像没用,因为苹果的充电器不是普通的胶水粘的!这尼玛是什么玩意呢?一个术语出来了—超声热和;高大上的技术啊;苹果做工就是好,我这租房这里家伙不全,简单的砟了几下没卵用!网上有人说房冰箱里冻下,好吧,扔冰箱里试试,下午了拿出来,确实有点凉,没锤子,尼玛看见啥用啥,大学时候的硬盘(里面有什么你懂的)拿来用吧,结果试了几下,还是没卵用。。。

  • 睡了一会觉。。。

  • 这是肯定没完啊,想当年在老家时,熬过多少个夜晚,只为我那组装的音响,往事不堪回首啦,一晃几年了!这是你逼我的,用我的电烙铁吧!我查,好使,那就继续吧,没一小会,我就把周围给全部踏平了,然后拿掉了这个盖子,识得庐山真面目:

  • 哈哈,这货是不是面目全非了:

  • 不忍心看了:

  • 接下来就是把电路板取出来了:

  • 还有工作要做,因为边缘需要处理,有的地方烙进去了

  • 不过问题不大,不严重,也不多:其实这材料挺硬的,需要小心,不要划到手,也不要划到电路板了

  • 哈哈,拿出来了:

  • 好好瞅瞅这家伙:

  • 这里是伤到的地方:

  • 这点小伤没啥的:

  • 看看背面吧:

  • 这是电源插头连接处,可以看出苹果也就是做个摆设啊,这地线根本就没连线!

  • 这货是出来了,我去哪里找个何时的外壳呢?卸开一个别的充电器看能不能放下吧,你看这电路板多简单呀,你就看中间这道黄线把,左边是220V的电源,一个整流桥,经过这个黄色的电感,得到低压,整流滤波,加个小灯完事,这比苹果的简单太多了,苹果的电路板上的东西我都看懂是啥,所以一个原装的充电器就是贵,今天就看到了,这是有原因滴!港版的是三角形的啊,放不进去:

  • 那就自己做个壳吧,就地取材,没办法,身在外不由己;上星期买的鼠标,纸壳子还在呢,感觉够硬,拿来用吧,大致比划了下,还行吧:

  • 给他留个盖子:

  • 感觉只有前盖没后盖不得劲啊,算了,画图纸,再来一个:

  • 我从小就喜欢看LED,记得第一次捡到一个绿色的led,用两节5号干电池点亮了一夜,陪着我入睡,陪着我醒来,那感觉很不错呢,所以我得装个指示灯!先看有地方没,刚好母插头旁边有地方,需要先取下她的铠甲才行呢:

  • 不得不再说一次,苹果做的就是精细,一个插头,也给她做个小外壳,这设计的真好,不忍心干掉他了都:

  • 去掉铠甲后,留下一条缝:

  • 这是前面,这条缝刚好可以用来穿过led的导线:

  • 这个LED小灯肯定是有着落啦,先找导线接通电源吧,这里我想灵活些,我可不想让她拖着长长的导线,所以又找来了废弃的光猫,上面有个插孔,刚好拿来用吧,就是这个黑色的啦:

  • 后面的为什么有一堆烟盒呢?烟瘾这么大!?什么啊,哥不抽烟好久了…这是大学时买的,刚才找LED分压电阻时拿出来的,看看里面有啥吧(认识么?):

  • 620欧姆的电阻:

  • 有电烙铁就好办事,取下来,焊接下,通电试试,这是物理老师教的哦,你忘了没?不要什么都做好了才通电,结果发现电路板已经挂了,那不就白费了嘛:

  • 我擦类,什么情况,怎么我的手机没有显示充电啊!我晕,尼玛的,老子很小心了啊,不可能啊,怀疑是光猫插头的事,立马查看电路板,看到了吗,那两个是连着的,再看我的电路,不一样,凭借经验断定我接的那个不是导电的,而是起固定作用的,莫慌,让我调换下导线,记得拔掉电源啊!!!

  • 然后开始接LED吧,测试了下正负极之后就确定了,LED的具体位置了,开搞吧,红色的太普遍啦咱不用,黄色的做指示灯的不多吧,白色是照明才用的,7彩色的我也没了,那就蓝色的吧,看放这里还行吧,也别无他选了呀:

  • 图纸早就剪好了,开始包装吧,是不是很丑啊:

  • 后面包好了,包前面吧,这里需要留孔,可是纸壳子不好搞,最终把留下的前盖给剪掉了,这算是个败笔啦,图纸是白画了!

  • 看到小灯了吗?

  • 好了,盖子都粘好了,通电吧:

  • 配备的是1k的电阻,高亮度的LED,是不是很亮了!

  • 看下后面吧,这个插头,可以拔下,剩下的就是个火柴盒那么大了:

  • 插上线试试充电吧:

  • 完工啦,来个华丽的转身:

  • 看看我这凌乱的工作台吧:

  • 哇,好乱啊!

  • ಥ_ಥ,电源拔了还能亮15s左右呢!电容在放电啦,不稀奇…

Done

不管别人怎么看,反正我是很享受这个过程啦,这是我的一个兴趣爱好而已!很遗憾没有学习物理电学,所以我也是个小白啦,仅仅知道一点电路罢了,让你们见笑了,O(∩_∩)O~~

这是别人拆绿点的过程 国外大神拆解苹果绿的充电器+深入解读其设计+绿点的由来

2015 中秋快乐!

  • ViewContrller 管理的 view 的大小是如何确定的?
  • 不同的系统版本 View 的大小一样吗?
  • 用 ViewContrller 管理的 view 的大小如何优雅的控制?

这些问题是我在写项目的时候遇到的,比较有感受;我写的是一个只支持横屏的 iPad 项目,view 比较多,因此按区域划分为好几个控制器分别管理,为了确保使用期间对象的安全存在,所以要把控制器保留住,如果不保留控制器,MRC 就会内存泄露,ARC下控制器就会释放,剩下他的 view 孤军奋战,这肯定不是你想要的结果,所以常规做法是保留控制器,然后把view添加上去,改变frame;这也可能是你现在正在使用的方法吧?!这里隐含了一个条件,在这个控制器里添加子控件的时候,是按一定的大小来写的,比如,A 控制器是一个详情展示,view 大小是(500,768),那么子视图的参考平面就是{(0,0),(500,768)}了,当然也可以超出边界,但这不是讨论的范围。

这种方法有什么问题?

其实这种方法可以,不过我在写某个模块时遇到了这样一个情形:

图 1 点击标签显示为图2

图 2

先看第一张图,很简单的九宫格布局;点击标签会出来一个浮层,样式大致是一样的,就是少了一个标签而已,所以立马想到的应该是重用了!(我可不想写两套布局)先别写哦,想下改怎么写?

按照引言部分的分析,大小已经确定了,不能重用!对于一个固执的人,没办法,就是要重用!因此考虑下怎么把这个 view 的大小变得可操控,然后子视图参考父视图而自动改变不就行了!

  • 查看view创建时的大小吧,因为这个很关键,子视图要参考的:

    1
    2
    在 viewDidLoad 里打印时,不论你模拟器的是什么方向,它默认总是竖屏(Portrait) size,也就是说在横屏模式下 width 和 height 是相反的,不是你想要的;本来猜测着vc的view的宽高会跟window的有关,通过测试把window 的宽高翻转也不行,反而会带来别的问题!经过测试,在viewDidAppear时,已经修改好了,变成横屏的大小了!又测试了下系统版本,发现:
    !!!iOS 8 已经没这个问题了,也就是说iOS 8 之后view的大小就是你的屏幕大小,而且与方向对应,比如横屏就是(1024,768);

问题来了,我们布局子视图的代码都写在 viewDidLoad 里,当我们在外部修改 view 的大小时 viewDidLoad 已经调用过了,因此这个外部指定的大小对于子视图来说然并卵啊!参照个毛线啊!有没有一举两得的办法,外部可以指定 子控制器 view 的大小,并且子控制器 viewDidLoad 里可以获取到指定的大小,而不是没有卵用的屏幕大小?

  • 由果索因 — view 从哪里来?

    当控制器的 view 被访问时就会调用 loadView 方法创建,然后紧接着调用 viewDidLoad ,
    我们创建添加子视图的代码一般也都写在这里面,因此如果 loadView 的时候 size 能正确的指定,
    我们就可以在 viewDidLoad 参照 view 的大小,布局子视图了!先打印看看结果吧,
    结果和 viewDidLoad 里打印的一样;所以可以从这里下手,
    在 loadVeiw 的时候指定一个 view 的 size,
    那么不可避免的就要加一个属性 viewSize 了!然后处理:

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
 - (instancetype)initWithViewSize:(CGSize)aSize
{
self = [super init];
if (self) {
self.viewSize = aSize;
}
return self;
}

- (void)loadView
{
[super loadView];
// 这里用了frame;用bound可能会导致origin不是0,0;导致 view 在其父视图上的位置有偏移;
CGRect rect = self.view.frame;
rect.size = self.viewSize;
self.view.frame = rect;
}

- (void)setViewSize:(CGSize)viewSize
{
_viewSize = viewSize;
if (self.isViewLoaded) {
CGRect rect = self.view.frame;
rect.size = self.viewSize;
self.view.frame = rect;
}
}
  • 有了上面的处理我们就可以在 viewDidLoad 以 view 的大小为参照布局子视图了!只需在外部调用的时候赋值就行了,不过需要注意,最好在外部使用到 view 之前给 viewSize 赋值,尽管已经实现了 setViewSize 方法!
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
// 这个 fetch 方法是根据索因找出对应控制器的,如果是刚创建的就需要添加为子控制器
- (UIViewController *)fetchAControllerwidthIndex:(NSUInteger)idx
{
if (idx >= self.btnArr.count) {
return nil;
}

BLBaseViewController *vc = self.controllers[idx];
if ([vc isKindOfClass:[UIViewController class]]) {
return vc;
}else{
//!!!创建控制器
vc = (BLBaseViewController *)[self createViewControllerwidthIndex:idx];
if (vc) {
//!!! 访问 view 属性之前指定 view 的大小
vc.viewSize = _contentView.bounds.size;
//!!! 添加控制器和 view
[self addChildViewController:vc];
[self.contentView addSubview:vc.view];
//!!! 这个显然就没什么用了;vc.view.frame = _contentView.bounds;
[self.controllers replaceObjectAtIndex:idx withObject:vc];
}
return vc;
}
}
  • 这样一来我就可以参考父视图写我的子控制器了,由于页面几乎一样,所以抽取了父类,页面布局都是在父类完成的,逻辑统一处理,两个子类就剩下寥寥数行而已!

加了一个属性,使用方法一样,还是在外部指定大小,最重要的是内部可以参考了!这达到了我预期的要求了!经过在项目中的使用,感觉比较理想!

在我的一个群里,小伙伴发了一道题,考察下算法,自己试着想了想,拿出来一起看下吧,下面是题目:

在漆黑的夜里,N 位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N 个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N 人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这 N 人尽快过桥。( ACM 难度系数 :5)

分析问题

面对毫无头绪的问题只能慢慢分析喽,首先把 N 个人需要的时间放到一个数组 T[n] 里面吧,时间的长短是不定的,因此先排为升序;既然是 N 个人,那就从 N = 1开始分析,找找规律吧:

1
2
3
4
N = 1; 当然需要花费 T[0] 喽(数组下标从 0 开始);
N = 2; 需要花费 T[0] + T[1],也就是两个人一起过去;
N = 3; 需要花费 T[0] + T[1] + T[2]; 很容易想到的是让最快那个人去送最慢的那个花费了 T[2];他返回来花费了 T[0],然后最快的这个和剩下的这个速度不是很快的一起过去花费了 T[1];
N = 4; 这个怎么过?陷入沉思之中...

这道题目的说白了就是送人,时间由最慢的那个人决定,而且送完一个,还要返回来接着送,所以我们不可能让一个走的慢的人来充当这个‘摆渡’的,既然如此,那么我们就从走的慢的人下手吧,因为他不能充当‘摆渡’的,那么他早晚都要‘坐船’走呀,所以 N = 4 就转化为花最少的时间送走‘坐船’的;(为了简化描述,按照时间升序的顺序将4人命名为a,b,c,d)下面分析下:

  1. 根据 N = 3 的经验,我们很容易想到让 a 去送每一个人,送完一个就返回,接着送最慢的,如此3次;
  2. 可是还有一种送法,a 送 b,a 回来;c 送 d,b 回来;a 送 b,也可以,而且时间不一定哪个短,因此要比较下哪个短;
  3. 4个人不可能就这两种送法啊,还有别的,不过时间都要比这两个长;

当 N > 4 呢?其实根据 N = 4 的情况来看,这就是一个如何先送走最慢的的2个人的问题,因此 N > 4 的时候,先不考虑中间的,把最快的,次快的,最慢的,此慢的拿出来摆渡,然后问题就变成了 N - 2 的问题了!当然可以写个递归了!

下面是主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int sumTimeNeed = 0;

while (size > 3) {
int time1 = a[1] + a[0] + a[size-1] + a[1] ;
int time2 = a[size-1] + a[0] + a[size-2] + a[0];
size -= 2;
sumTimeNeed += MIN(time1, time2);
}

if (size == 3) {
sumTimeNeed += a[0] + a[1] + a[2];
}else if (size == 2){
sumTimeNeed += a[1];
}else if (size == 1){
sumTimeNeed += a[0];
}

return sumTimeNeed;

欢迎去我的 GitHub 建议


1.最古老的过河问题

一个农民携带一只狼,一只羊和一棵白菜,要借助一条小船过河。小船上除了农民只能再带狼、羊、白菜中的一样。而农民不在时,狼会吃羊,羊会吃白菜。农民最少需要几趟才能安全过河呢?

2.人鬼殊途

三个鬼与三个人都要过河。河中只有一条小船,可容两人(鬼)。而且无论在船上或在岸上,每边的鬼数量如果多于人,鬼就会把人吃掉。最少需要几趟才能安全过河

3.道士与和尚

四个道士与四个和尚四个老道士与四个和尚分别在一条河的两岸,都要到河的对岸去。河中只有一条小船,可容两人。只有一个道士和一个和尚会划船。而且无论在船上或在岸上,道士的数量都不能超过和尚的数量。最少需要几趟才能安全过河

4.夫妻过河

两对夫妻两对夫妻要过河,河中只有一条小船,可容两人。两个丈夫都不愿让自己的妻子和另一个男人在一起,除非自己也在场。
最少需要几趟才能安全过河?

5.一家人及警察与犯人

现有一条河,共有八个人要过河,分别是爸爸,妈妈,两个儿子,两个女儿,一个警察,一个犯人.现有一条木伐,一次最多载两个人,在这八个人中,有妈妈,爸爸,警察会开船,即这个船上必须有爸爸,妈妈,警察三个中的一个,船才会开动.船过去无法自动回来.并且要避免以下三件事发生,1,警察不在犯人会伤害一家六口.2,爸爸不在,妈妈会伤害儿子.3,妈妈不在,爸爸会伤害女儿.
最少需要几趟才能安全过河

6.虎毒不食子

三只大老虎A、B、C和各自的小老虎a.b.c;其中只有A、B、C和a会划船;如果小老虎不和自己的母亲在一起就会被其他大老虎吃掉;只有一条船;船上可以坐一只大老虎和任意的小老虎;问:最少需要几趟才能安全过河

7.八仙过海

铁拐李 、汉钟离 、 张果老 、蓝采和 、 何仙姑 、 吕洞宾 、 韩湘子 、 曹国舅一同过海。小船只能容下三人,而且只有道铁拐李 、汉钟离、韩湘子会划船。如果韩湘子不在,曹国舅会攻击所有人;如果铁拐李不在,汉钟离会攻击张果老和蓝采和;如果汉钟离不在,铁拐李会攻击何仙姑 、 吕洞宾。请问最少需要几趟才能安全过海?

有兴趣的快去 AC 吧!

我们的 App 要展示广告,形式为几张循环滚动的图片,俗称 Banner,这里我称之为轮播图。 我写了两个版本一个 EY 版和一个 EZ 版,区别是实现方式的不同:EY 使用 ScrollerView,最多会有 3 个子 view ,EZ 使用 CollectionView,她有重用机制,所以最多会有 2 个子 view;轮播的触发使用了定时器,这个已经解决了循环引用问题,直接使用了,不清楚的可以移步 这里 !下面分别介绍下:

效果

EY 实现思路

在 ScrollView 上添加 3 张 ImageView 展示图片,为了节省内存,所以最多创建 3 张就行了(其实 2 张也是可以的,这个后续继续优化,版本且定为 EYS ,哈哈她是 EY 的加强版)!

比如现在有6张([1,2,3,4,5,6])图片需要显示,那么首先我会配置出来需要显示的图片索引,放在一个数组里[6,1,2]当作数据源,然后调整偏移量让 ScrollView 滚动到中间,那么看到的就是第一张了,此时向右滑动看到最后一张,向左滑动看到第二张;如下流程:

* ScrollView 滚动到中间显示[1],数据源里为[6,1,2],刷新视图,此时可以滑动
* 向右滑动显示[6]之后就重新配置为[5,6,1],然后刷新视图让 ScrollView 滚动到中间
* 向左滑动显示[2]之后就重新配置为[1,2,3],然后刷新视图让 ScrollView 滚动到中间

同时还需要处理一张图片的情况,如果就 1 张,那么不可滑动,数据源为[1];

可以看出,中间那一张是当前看到的,他的左右两侧会放上合适的图片,这样滑动的时候就感觉像是一直在循环♻️一样了;

先看使用方法吧

1
2
3
4
5
6
7
8
9
10
11
12
EYCarouseImageView *carouseY = [[EYCarouseImageView alloc]initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, 240) animationDuration:3];

[carouseY resetEasyURLArr:@[@"http://pic.nipic.com/2007-11-09/2007119122519868_2.jpg",
@"http://pic26.nipic.com/20121223/9252150_195341264315_2.jpg",
@"http://b.hiphotos.baidu.com/album/pic/item/cb8065380cd79123c6f9b8dead345982b2b7807a.jpg?psign=c6f9b8dead345982b2b7d0a20cf431adcaef76094b36a442",
@"http://pic.nipic.com/2007-11-09/2007119121849495_2.jpg"]];

[self.view addSubview:carouseY];

[carouseY didClickedEYCarouseImageView:^(NSUInteger idx) {
NSLog(@"----%lu",(unsigned long)idx);
}];

内部实现

  • 配置下 ScrollView,添加手势处理点击;注册内存警告⚠的通知;
  • 处理控制轮播的属性: autoScrollTimeInterval,allowAutoScroll;
  • 配置定时器:
1
2
3
4
5
6
7
8
//一个时间间隔后开始轮播
- (void)resumeAutoScroll
//销毁定时器
- (void)invalidateTimer
//重置timer;
- (void)resetTimer
//定时器触发的自动轮播;
- (void)autoScrollLoop
  • 准备数据源
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
//获取下一个可用的索引
- (NSInteger)getValidNextPageIndexWithPageIndex:(NSInteger)currentPageIndex {
if(currentPageIndex == -1) {
return self.totalCount - 1;
} else if (currentPageIndex == self.totalCount) {
return 0;
} else {
return currentPageIndex;
}
}

//准备当前需要显示的索引数组;
- (NSArray *)prepareNeedShowPageIdxArr
{
NSArray *idxArr = nil;

if (_urlArr.count == 1) {
idxArr = @[@(0)];
}else{
NSInteger prevPageIndex = [self getValidNextPageIndexWithPageIndex:self.currentIdx - 1];
NSInteger nestPageIndex = [self getValidNextPageIndexWithPageIndex:self.currentIdx + 1];
idxArr = @[@(prevPageIndex),@(self.currentIdx),@(nestPageIndex)];
}
return idxArr;
}
  • 刷新的视图的时候需要更新图片
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
- (void)updateImage4URLIdx:(NSUInteger)uidx
{
NSNumber *idxNum = [self.showURLIdxMap objectForKey:[self getMapKey:uidx]];

if (idxNum) {
NSUInteger idx = [idxNum integerValue];
UIImageView *imgView = self.contentViewArr[idx];
NSString *url = self.urlArr[uidx];

UIImage *img = [[SDImageCache sharedImageCache]imageFromMemoryCacheForKey:url];
if (img) {
imgView.image = img;
}else{
imgView.image = self.placeHolderImage;
// 内存里没有,就查本地;
__weak __typeof(self)weakSelf = self;
[[SDImageCache sharedImageCache]queryDiskCacheForKey:url done:^(UIImage *image, SDImageCacheType cacheType) {
// 查到了就放内存,刷新下view;
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (image && strongSelf) {
[strongSelf updateImage4URLIdx:uidx];
}
}];
}
}
}
  • 重写数据源处理必要的操作
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
- (void)setUrlArr:(NSArray *)urlArr
{
// copy个新的,防止外部改变了,影响了轮播图
_urlArr = [urlArr copy];
self.totalCount = _urlArr.count;
self.currentIdx = 0;
self.scrollEnabled = self.totalCount > 1;
// 配置子view;
[self prepareScrollViewContentDataSource];

if (self.totalCount > 0) {
// 更新子view显示的图片;
[self updateSubViews];
}
// 重设下,更新自动轮播的状态;
self.allowAutoScroll = self.allowAutoScroll;
// 下载图片;
for (int i = 0; i < _urlArr.count; i ++) {
NSString *url = _urlArr[i];
__weak __typeof(self)weakSelf = self;
[[SDWebImageManager sharedManager]downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:NULL completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
// 更新下当前显示的图片;
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf && image) {
if (i < strongSelf.urlArr.count) {
[strongSelf updateImage4URLIdx:i];
}
}
}];
}
}

- (void)updateSubViews
{
NSArray *idxArr = [self prepareNeedShowPageIdxArr];

NSInteger counter = 0;

for (NSNumber *tempNumber in idxArr)
{
NSInteger tempIndex = [tempNumber integerValue];
[self.showURLIdxMap setObject:@(counter) forKey:[self getMapKey:tempIndex]];
[self updateImage4URLIdx:tempIndex];
counter++;
}
// 显示中间的;
if (self.totalCount > 1)
{
[self setContentOffset:CGPointMake(self.frame.size.width, 0)];
}
}
  • 重新配置数据源逻辑
1
2
3
4
5
6
7
8
9
10
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat contentOffsetX = scrollView.contentOffset.x;

if(contentOffsetX >= (2 * CGRectGetWidth(scrollView.frame))) {
[self showNextPage];
}else if(contentOffsetX <= 0) {
[self showPreviousPage];
}
}
  • 处理点击事件
1
2
3
4
5
6
7
8
9
10
11
12
- (void)tapScrollView:(UITapGestureRecognizer *)sender
{
[self resumeAutoScroll];

if (self.DidClickedBlock) {
self.DidClickedBlock(self.currentIdx);
}

if (self.carouseDelegate && [self.carouseDelegate respondsToSelector:@selector(eyCarouseImageView:didClickedImageView:)]) {
[self.carouseDelegate eyCarouseImageView:self didClickedImageView:self.currentIdx];
}
}

EZ 实现思路

思路和EY是一样的,只不过这个使用 CollectionView 来实现,数据源的配置是一样的,就是减少了一个展示图片的 ImageView !

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
EZCarouseImageView *carouse = [[EZCarouseImageView alloc]initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, 240) animationDuration:3];

[carouse resetEasyURLArr:@[@"http://cdn.duitang.com/uploads/item/201110/09/20111009155438_ddWci.jpg",
@"http://pic27.nipic.com/20130220/11588199_085521216128_2.jpg",
@"http://a0.att.hudong.com/57/78/05300001208815130387782748704.jpg",
@"http://pic29.nipic.com/20130506/3822951_101843891000_2.jpg"]];

[self.view addSubview:carouse];

[carouse didClickedEZCarouseImageView:^(NSUInteger idx) {
NSLog(@"--点击:--%lu",(unsigned long)idx);
}];

内部实现

  • 配置下collectionview的Layout;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (instancetype)initWithFrame:(CGRect)frame
{
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];

layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 0;
layout.itemSize = frame.size;
layout.sectionInset = UIEdgeInsetsZero;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

self = [super initWithFrame:frame collectionViewLayout:layout];
if (self) {
[self initialization];
[self registerMemoryWarningNotification];
}
return self;
}
  • 配置定时器
  • 注册MemoryWarningNotification;内存警告后加载当前显示的图片;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)registerMemoryWarningNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(needLoadImageFromDisk2Memory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}

//内存警告后,从本地加载当前需要显示的图片到内存;
- (void)needLoadImageFromDisk2Memory
{
NSInteger idx = [self itemMapedURLidx:self.currentIdx];
if (idx != NSNotFound) {
[self fetchImageForCellWithURLidx:idx];
}
}
  • 重写数据源处理跟EY类似
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
- (void)setUrlArr:(NSArray *)urlArr
{
// copy个新的,防止外部改变了,影响了轮播图
_urlArr = [urlArr copy];
if (_urlArr && _urlArr.count > 0) {

self.currentIdx = 0;
// 更新映射的索引;
[self updateMapedIdx:[self prepareNeedShowPageIdxArr]];
[self reloadData];

if ([self totalCount] > 1) {
self.scrollEnabled = YES;
// 显示第一张;假如有n(n > 1)帧,那么[n-1,0,1]
[self scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
}else{
self.scrollEnabled = NO;
}
// 重设下,更新自动轮播的状态;
self.allowAutoScroll = self.allowAutoScroll;
// 下载图片;
for (NSString *url in _urlArr) {
__weak __typeof(self)weakSelf = self;
[[SDWebImageManager sharedManager]downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:NULL completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
// 更新下当前显示的图片;
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf && image) {
[strongSelf reloadItemsAtIndexPaths:[strongSelf indexPathsForVisibleItems]];
}
}];
}
}else{
// 重设下,更新自动轮播的状态;
self.allowAutoScroll = self.allowAutoScroll;
[self reloadData];
}
}
  • 看下UICollectionView 的 DataSource吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
EZCarouseCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:EZCarouseCellReuseIdentifier forIndexPath:indexPath];
// 通过item获取对应的数组索引;
NSInteger idx = [self itemMapedURLidx:indexPath.item];
UIImage *image = nil;
if (idx != NSNotFound) {
image = [self fetchImageForCellWithURLidx:idx];
}
// 找不到图片就用placeHolder
if (!image && self.placeHolderImage) {
image = self.placeHolderImage;
}
cell.imgView.image = image;
return cell;
}
  • 根据图片的数量配置cell个数;
1
2
3
4
5
6
7
8
9
10
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if ([self totalCount] == 1){
return 1;//一个不让滚动
}else if([self totalCount] > 1){
return 3;//超过一个就用3个,然后显示中间的
}else{
return 0;
}
}
  • 何时更新map的索引,更新显示的图片?
1
2
3
4
5
6
7
8
9
10
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat contentOffsetX = scrollView.contentOffset.x;
//显示了第3个item,或者第1个item时就要更新下显示的图片索引数组
if(contentOffsetX >= (2 * CGRectGetWidth(scrollView.frame))) {
[self showNextPage];//这个会更新视图
}else if(contentOffsetX <= 0) {
[self showPreviousPage];//这个会更新视图
}
}
  • 点击事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//点击支持代理和block回调
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if (self.carouseDelegate && [self.carouseDelegate respondsToSelector:@selector(ezCarouseImageView:didClickedImageView:)]) {
[self.carouseDelegate ezCarouseImageView:self didClickedImageView:[self itemMapedURLidx:indexPath.item]];
}
if (self.DidClickedBlock) {
self.DidClickedBlock([self itemMapedURLidx:indexPath.item]);
}
}

- (void)didClickedEZCarouseImageView:(void (^)(NSUInteger))block
{
self.DidClickedBlock = block;
}

核心的逻辑都在这了,欢迎在 github 上提问题!