iOS 如何放大按钮点击热区

Apple的iOS人机交互设计指南中指出,按钮点击热区应不小于44x44pt,否则这个按钮就会让用户觉得“很难用”,因为明明点击上去了,却没有任何响应。

但我们有时做自定义Button的时候,设计图上的给出按钮尺寸明显要小于这个数。例如我之前做过的自定义Slider上的Thumb只有12x12pt,做出来后我发现自己根本点不到按钮……

这个问题在WWDC 2012 Session 216视频中提到了一种解决方式。它重写了按钮中的pointInside方法,使得按钮热区不够44×44大小的先自动缩放到44×44,再判断触摸点是否在新的热区内。

1
2
3
4
5
6
7
8
//官方在视频中给出的示例源码
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)withEvent
{
CGFloat widthDelta = 44.0 - bounds.size.width;
CGFloat heightDelta = 44.0 - bounds.size.height;
bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
return CGRectContainsPoint(bounds, point);
}

###不过这里有两个小问题.

  • 当定义的Button.frame大于44×44时,这里仍然会将热区缩小至44×44,从而导致超过44×44的按钮热区失去响应。
  • bounds变量未定义

修正后的代码如下:

1
2
3
4
5
6
7
8
9
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect bounds = self.bounds;
//若原热区小于44x44,则放大热区,否则保持原大小不变
CGFloat widthDelta = MAX(44.0 - bounds.size.width, 0);
CGFloat heightDelta = MAX(44.0 - bounds.size.height, 0);
bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
return CGRectContainsPoint(bounds, point);
}

27个提升效率的iOS开源库推荐

####本文转自Medium

我热爱开源,更喜爱那些花费宝贵的业余时间来创造奇迹的开发者们,感谢他们将自己辛苦劳动的成果无偿分享给大家。开源作者和贡献者们,你们太了不起了,感谢你们所付出的一切。

鉴于我是收集不全会死星人,这里我根据个人喜好选取了一些iOS的开源库。这些项目的顺序完全随机,每个都棒到爆。绝大多数支持CocoaPods,所以将它们添加到你的Xcode项目中只是小事一桩。

在本文末尾你可以找到一个长文慎入版本——一个只包含题目与项目链接的简单列表。如果你觉得这篇文章有用的话,请把它分享给其他做iOS开发的小伙伴们,好酒也怕巷子深。


DZNEmptyDataSet

DZNEmptyDataSet算是一个很标准的iOS内建方式,适合用来处理空的table view和collection view。默认情况下,如果你的table view为空,屏幕就为空,这样一来用户体验并不理想。

使用这个库,你只需遵守一些协议,iOS就会自动将collection view处理完善,并将用户消息以合适美观的方式显示出来。每个iOS项目都可以自动处理,不用再一一费神。


PDTSimpleCalendar

你的应用需要一个简单美观且正常运行的日历组件吗?现在有了——PDTSimpleCalendar也许可以算是iOS最棒的日历组件了。你可以在各个方面对它进行定制,无论是运行逻辑还是外观方面。


MagicalRecord

他们都说:Core Data简洁易用。他们又说:很棒很好用噢。哈?真的吗,苹果?添加到各个项目中的大量样板数据完全不符合简洁而易用的标准。更别提添加、移除和更新那一大堆的entities、保存context、按不同环境创建不同的Core Data堆栈等等。当然,我很喜欢Core Data,但是苹果真的可以把它更好地简化一下——使用MagicalRecord方式。

MagicalRecord就像是给Core Data提供了一层外包装,隐藏掉所有不相关的东西。如果你曾经使用过active record模式(比如Ruby on Rails),你就知道了。倾情推荐,在应用中使用Core Data的童鞋们可一定要试试。


Chameleon

如果你已经看到这里了,我猜你更有可能是个程序猿,而不是设计狮。这里有个东东很适合你。

Chameleon是一个iOS的色彩框架。它运用现代化flat color将UIColor扩展地非常美观。我们还可以通过它运用自定义颜色创建调色板。它还有很多功用,请浏览readme。如果你想要应用美观的话,一定要把这个库加到项目里。


Alamofire

Alamofire是一个简洁的网络库,用Swift语言编写。你是否曾经使用过AFNetworking呢?Alamofire是它的小弟。更年轻更时尚,当然(AFNetworking是用Objective-C编写的)。

如果你需要进行网络的相关工作,诸如下载、上传与获取JSONs等等的话,Alamofire正是你所需要的。GitHub上8000人次推荐,一定不会错。


TextFieldEffects

你不觉得标准的UITextField有些枯燥么?我也这样想——来认识一下TextFieldEffects吧!废话不多说,只要看几个例子:

是啊,都是些简单的dropin控制器。你甚至可以在storyboard中使用IBDesignables。

不幸地是:这个库不支持CocoaPods(如果你来自未来,而这一情况什么时候会改变的话,一定要在Twitter上告诉我),但是它支持Carthage。你只管从GitHub上下载项目,把它放入你的workspace就行了。


GPUImage

你曾经写过照相机应用吗?如果没有的话,很快你一定会遇到这个库的。

GPUImage为我们提供了一个GPU加速的照相机效果(同时支持照片与视频),而且处理速度飞快。在App Store中,使用这个库的应用数以百计。我有一个应用也用到了GPUImage。 它在GitHub上获得了8869个star,而且还在增长。


iRate

想要在App Store中获得更多评论的最佳方式是什么?想要回答这个问题,我缺乏切实数据,但如果必须猜一下的话,我会建议问问用户。也许这样做有点老套——大多开发者现在都有创建定制的应用内置alert。

但是如果你没有时间,或者不想从头实现的话,最好用一下iRata。这个就是iRata——一个小型库——你可以把它放入项目中,把问卷调查什么的都忘记吧,iRate会在恰当的时候为你解决这个问题。


GameCenterManager

无论你喜不喜欢单例模式,管理一个GameCenter都比我们已知的其他相反模式要好很多。(你的游戏只有一个GameCenter,对吗?)

说实话,在iOS上管理GameCenter vanilla并不算难,但是有了这个库会更简单也更快。好上加好不是更好么。

我在我一个游戏中用到了这个,体验很好。


PKRevealController 2

这个要注意,真的很棒!是我最喜爱的iOS控件之一。PKRevealController是一个可以滑动的侧边栏菜单(可向左、向右或者同时向两侧),只需手指轻轻一点(或者按一下按钮,但是这样滑动时不够炫酷)。

我试过一些可以提供这类控制的其他库,而PKRevealController是最棒的。安装简便,高度定制且对手势识别良好。可以当做一个标准控件用在iOS SDK中。


SlackTextViewController

你曾经用过Slack iOS应用吗?如果你在较大的软件公司工作,也许会用过。对那些没用过的人呢?—?Slack令人激动。用到Slack的应用也是这样,尤其是用作极佳、定制的文本输入控制时。这时你有了一个现成可用在应用中的代码。

自适应文本区域?试一下。

手势识别、自动填充、多媒体合并?试一下。

快速drop-in解决方案?试一下。

其他还想要什么?


RETableViewManager

RETableViewManager可以帮助你进行动态创建与管理table views。它给我们提供了预定义cells(bool类型、文本、日期等等——请看下面的截图),但是你还可以创建自定义views,并与默认视图一同使用。

左侧截图显得非常古板!在storyboard中没有这个库的时候,你能做的就是这些了,但是有时候代码比可视化编辑器要好。


PermissionScope

用这个库可以在询问用户前,就告知用户所需的系统权限,为用户带来更好的体验。接受度更高—>更多活跃用户->更高的留存率->数据更好->下载率更高。墙裂推荐pod。


SVProgressHUD

这张图片在正常加载,无需等待太久或者刷新页面。这正是SVProgressHUD在你的应用中的表现。如果你需要定制化的等待提示器,这个就是了(也许是最好的)。


FontAwesomeKit

Font Awesome很棒,有了它你可以很容易地给项目加字体,使用方式繁多。


SnapKit

喜欢自动布局吗?当然喜欢!至少在storyboard中创建时会喜欢。 在代码中纯手工创建约束灰常痛苦,但幸运的是我们有了SnapKit,在board中用上它,你可以简单直观地编写约束了。


MGSwipeTableCell

这是另一个常见于很多应用中的UI组件,苹果应该考虑在标准的iOS SDK中加入一些类似的内容。Swipeable表格cell是这个pod的最佳描述,也是最好的。

这只是其中三个动画类型,还有更多变化,请查看readme。


Quick

用于Swift中的单元测试(也可用于Objective-C),与Xcode整合在一起。如果你是Objective-C的粉丝,我建议用Specta代替这个,但是对Swift使用者来说,Quick是最佳选择。


IAPHelper

应用内付费给我们提供了很多样本代码,而这个库丢掉了那些代码,将金钱交易相关的大多通用任务做了简单的封装。


ReactiveCocoa

好吧,这是个小怪物。

ReactiveCocoa并不像列表中其他库一样,它不是小型的drop-in项目。ReactiveCocoa给我们带来了一个迥异的编程风格与结构,它是基于信号与数据流的。首先你需要忘掉已知的一切,才能理解它的工作方式。很有难度,但是价值不斐。

在这里教ReactiveCocoa有点不合适,但是如果你感兴趣的话,我会提供一些好源:


SwiftyJSON

使Swift的JSON解析变得简单。


Spring

使动画在简单性、可链接性与声明性方面有所提高。


FontBlaster

载入定制字体时更简单。


TAPromotee

交叉推广应用是你可以免费实现的最佳市场推广策略之一。使用这个库做起来非常简单,不用都不可能——将TAPromotee加入你的podfile中,免费配置与享受更多下载吧。


Concorde

你在应用中载入了一堆jpeg吗?有了Concorde,你可以用更好的方式来解决,这是一个很大的进步。


KeychainAccess

管理Keychain接入的小助手。


iOS-charts

最后一个,但绝不是最不重要的——iOS图表库!非常有用而且美观,这里我无需赘言。向下看,你就知道用它可以做什么了。

没错,一切都变成了drop-in组件了(也许是“code-in组件)。

不幸的是,它还不支持CocoaPods,所以你得手动把它拽到你的Xcode workspace里面去。

调用私有方法开启关闭蓝牙

先导入IOBluetooth.framework

然后调用我写的方法

MTBluetooth.h

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
@interface MTBluetooth : NSObject

/**
* 蓝牙是否可用
*
* @return
*/
+ (BOOL)isAvaliable;

/**
* 是否开启
*
* @return <#return value description#>
*/
+ (BOOL)isPower;

/**
* 开启蓝牙
*/
+ (void)openBluetooth;

/**
* 关闭蓝牙
*/
+ (void)closeBluetooth;

@end

MTBluetooth.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
#import "MTBluetooth.h"


// private methods
int IOBluetoothPreferencesAvailable();

int IOBluetoothPreferenceGetControllerPowerState();
void IOBluetoothPreferenceSetControllerPowerState(int state);

int IOBluetoothPreferenceGetDiscoverableState();
void IOBluetoothPreferenceSetDiscoverableState(int state);

// dry
int BTSetParamState(int state, int (*getter)(), void (*setter)(int)) {
if (state == getter()) {
return EXIT_SUCCESS;
} else {
setter(state);

usleep(1000000); // Just wait, checking getter even in 10 seconds gives old result
return EXIT_SUCCESS;
}
}

// short names
typedef int (*getterFunc)();
typedef int (*setterFunc)(int);

#define BTAvaliable IOBluetoothPreferencesAvailable

#define BTPowerState IOBluetoothPreferenceGetControllerPowerState
int BTSetPowerState(int state) {
return BTSetParamState(state, BTPowerState, IOBluetoothPreferenceSetControllerPowerState);
}

#define BTDiscoverableState IOBluetoothPreferenceGetDiscoverableState
int BTSetDiscoverableState(int state) {
return BTSetParamState(state, BTDiscoverableState, IOBluetoothPreferenceSetDiscoverableState);
}

@implementation MTBluetooth

+ (BOOL)isAvaliable {
return BTAvaliable();
}

+ (BOOL)isPower {
if ([self isAvaliable]) {
return BTPowerState() == 1;
}
return NO;
}

+ (void)openBluetooth {
BTSetPowerState(1);
}

+ (void)closeBluetooth {
BTSetPowerState(0);
}

@end

设计师也要学物理学

####本文转自UI.CN
我们会把从小到大对这个世界的认识映射到屏幕世界中。所以从事物原理的角度出发,探究一下运动曲线。

本文并不深入物理定律的本身,只讲述运动曲线的运用,曾经上课睡觉的同学也不必担心。

先引用下百科神马是物理:物理学是研究物质运动最一般规律和物质基本结构的学科。作为自然科学的带头学科,物理学研究大至宇宙,小至基本粒子等一切物质最基本的运动形式和规律,因此成为其他各自然科学学科的研究基础。它的理论结构充分地运用数学作为自己的工作语言,以实验作为检验理论正确性的唯一标准,它是当今最精密的一门自然科学学科。(猜你们也没仔细看,总之就是物理是一门非常牛逼的学科。)

我一直认为动效设计的本身是人机交互中非常重要的环节,并且我们会把从小到大对这个世界的认识映射到屏幕世界中。比如最常见的抓、扔、移动等操作,都是婴儿时期就习得到的基本本能,那么这些动作经常会被我们本能的运用到长大后接触到的屏幕操作中。

上一篇中已经提到了一些动效设计的原则(传送门:UI中国 ),以及简单的写到了动效设计怎么样的表达让用户更加舒适。恩,今天要深入这个话题,动效设计师如何运用物理理论来让动画曲线更加贴近现实世界,如何让你的动画效果更加的如烟花般炫丽。


###一.牛顿第一定律 又称惯性定律 由牛顿在1687年提出

任何物体都要保持匀速直线运动或静止状态,直到外力迫使它改变运动状态为止。

什么是保持匀速直线运动:

可以感觉到,它的运动似乎有些无聊,因为我们生活的这个世界存在引力、空气阻力、摩擦力等等。那就看一下最常见的外力因素——摩擦力,现实世界中的大部分运动都有它的身影。

1.摩擦力 F=μ×FN

阻碍物体相对运动(或相对运动趋势)的力叫做摩擦力。

摩擦力是个宜动宜静的乖宝宝~它有两种形态:

A 静摩擦力:两个相互接触的物体,当其接触表面之间有相对滑动的趋势,但尚保持相对静止时,彼此作用着阻碍相对滑动的阻力,这种阻力称为静滑动摩擦力,简称静摩擦力。

摩擦力比较大的时候会发生,由于相对没动,所以保持和传送带一起运动。

B 动摩擦力:两接触物体之间存在相对滑动时,其接触面上产生阻碍对方滑动的阻力称为动滑动摩擦力。

可以看到由于材质的不同,或者又加上制动等因素,只要记住物体表面粗糙及压力越大,摩擦力越大,结合下面的牛顿第二定律
就可以模拟出一些不同感觉的运动曲线了。


###二.牛顿第二定律 F=ma 由牛顿在1687年提出

物体加速度的大小跟作用力成正比,跟物体的质量成反比,且与物体质量的倒数成正比;加速度的方向跟作用力的方向相同。

简单的说就是——物体越轻或者受到的力越大加速度越大:


###三.牛顿第三运动定律 F=-F’ 由牛顿在1687年提出

相互作用的两个物体之间的作用力和反作用力总是大小相等,方向相反,作用在同一条直线上。

常见的比如说弹撞击,拉、推重物等等。反正哪哪都有它,不要忘记它就好。

既然提到牛三那就一定要提到一个它最重要的衍生定律了:

1.动量守恒定律 m1v1+m2v2=m1v1′+m2v2′

一个系统不受外力或所受外力之和为零,那么这个系统的总动量保持不变。

动量守恒定律最初是牛顿第三定律的推论, 但后来发现它们的适用范围远远广于牛顿定律。简单的描述就是,两个速度不同的物体碰撞之后会产生速度上的『传递』,打台球的同学应该有更深的感触。


###四.阿基米德定律 F=γV

浸入静止流体(气体或液体)中的物体受到一个浮力,其大小等于该物体所排开的流体重量,方向垂直向上并通过所排开流体的形心。

所以水中物体弹出速度与物体的体积、质量成正比~而且会越靠近水面越快。看下曲线图:

可以发现浮起来的过程因为外力的方向不断变化以致有些类似弹性曲线,继而联想到了胡克(弹性)定律:


###五.胡克定律 F=-k·x 又称弹性定律 由胡克在1678年提出

弹簧在发生弹性形变时,弹簧的弹力F和弹簧的伸长量(或压缩量)x成正比,即F= k·x 。k是物质的弹性系数,它只由材料的性质所决定,与其他因素无关。负号表示弹簧所产生的弹力与其伸长(或压缩)的方向相反。

那么由胡可定律结合牛顿第二定律F=ma,我们就可以得出弹性曲线来,这里的关键因素就是物体质量、弹性系数、伸长量来决定。

*『制作』:描述弹性曲线通常的三个关键参数分别是:频率、过量、持续时间。


###六.表面张力

液体表面任意二相邻部分之间垂直于它们的单位长度分界线相互作用的拉力。表面张力的形成同处在液体表面薄层内的分子的特殊受力状态密切相关。

表面张力是动效设计中和阿基米德定律一样容易遇到的流体力学里的知识。算式比较复杂,不再深究。张力的运用很广泛,那么我们做动效最常遇到的通常是融合效果:

总结

物理的运用基本上在我们生活的世界中并没有理想状态,所以了解原理的同时,设计狮们请一定要愉快的观察我们身边的世界,真实世界才是最好的学习案例。

最后的最后如果文中对科学道理的阐述有什么错误的地方,希望学霸同学们可以指出~

附一:压箱底的缓动函数曲线 http://easings.net/zh-cn(如打不开,请使用科学上网法)

附二:写的过程中也看到阿里U一点的文章——动效设计的物理法则,重点的写了牛顿第一第二定律,感兴趣的的同学们可以点过去拜读下~

蓝牙BLE后台自动重连

关于ios BLE在后台自动重连的问题

找到一个国外网站是这么描述的

Re: How to make the BTLE APP to auto connect with BTLE device when iPhone4S power on

When backgrounding, you can still scan and connect to a LE peripheral. You can scan for peripherals using scanForPeripheralsWithSerives by stating what service you are looking for in a Peripheral. You cannot do a general scan without stating at least one service in the passed dictionary. If you have a CFUUIDRef from a previous connection, you can call retrievePeripherals with the CFUUIDRef. Your delegate will receive a call to didRetrievePeripherals with a CBPeripheral. With the CBPeripheral you can then connect using connectPeripheral.

如何获取顶层UIViewController

Objective-c版本:

1
2
3
4
5
6
7
8
- (UIViewController *)appRootViewController{
UIViewController *appRootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
UIViewController *topVC = appRootVC;
while (topVC.presentedViewController) {
topVC = topVC.presentedViewController;
}
return topVC;
}

swift 2.0版本:

1
2
3
4
5
6
7
8
9
10
11
extension UIApplication {

/// 顶层VC
static var appRootViewController: UIViewController? {
var topVC = UIApplication.sharedApplication().keyWindow?.rootViewController
while topVC?.presentedViewController != nil {
topVC = topVC?.presentedViewController
}
return topVC
}
}

获取当前的wifi SSID

version: swift 2.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension UIDevice {

/// wifi SSID
static var WIFISSID: String {
let interfaces: CFArray? = CNCopySupportedInterfaces()
if interfaces == nil { return "" }

let if0: UnsafePointer<Void>? = CFArrayGetValueAtIndex(interfaces, 0)
if if0 == nil { return "" }

let interfaceName: CFStringRef = unsafeBitCast(if0!, CFStringRef.self)
let dictionary = CNCopyCurrentNetworkInfo(interfaceName) as NSDictionary?
if dictionary == nil { return "" }

if let SSID = dictionary?[kCNNetworkInfoKeySSID as String] as? String {
return SSID
}
return ""
}


}