1、使用动画切换window的根控制器
UIView.transition(with: self.window!, duration: 0.5, options: UIViewAnimationOptions.transitionCrossDissolve, animations: {
let oldState = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(false)
let mainNav = UIStoryboard.main().viewController(withID: .MainNavigationController)
self.window?.rootViewController = mainNav
UIView.setAnimationsEnabled(oldState)
}, completion: { (finish) in
})
2、KVO高级用法
适用于NSArray,可以实现对数据元素的求和、最大值、最小值、平均值等计算,还可以剔除重复的数据,对字典数组快速找出对应的key值,修改placeHolder的字体大小和颜色。具体参考:
http://www.thinksaas.cn/topics/0/347/347021.html
3、颜色转图片
static func creatImageWithColor(color:UIColor)->UIImage{
let rect = CGRect.init(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(color.cgColor)
context!.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
4、GCD用法
let time:TimeInterval = 1.5
let dispatchTime = DispatchTime.now() + time
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
}
5、单例
class BankListModel {
// 单例
static let shared = BankListModel.init()
private init(){}
func test1() {
}
}
6、获取当前的视图控制器
func getCurrentVC() -> UIViewController? {
//获取当前的控制器
let mainNavVC = window?.rootViewController as? UINavigationController
var currentVC = mainNavVC?.viewControllers.last
while currentVC?.presentedViewController != nil {
currentVC = currentVC?.presentedViewController
if currentVC is UINavigationController {
currentVC = (currentVC as! UINavigationController).visibleViewController
}else if currentVC is UITabBarController {
currentVC = (currentVC as! UITabBarController).selectedViewController
}
}
return currentVC
}
7、圆角图片
class CWRoundImageView: UIImageView {
public var borderColor:UIColor = UIColor.white
public var borderWith:CGFloat = 0.0
public var cornerRadius:CGFloat = 0.0
public func addMaskToBounds(maskBounds: CGRect) {
let w = maskBounds.width
let h = maskBounds.height
let size = maskBounds.size
let scale = UIScreen.main.scale
let bigRect = CGRect.init(x: 0, y: 0, width: w, height: h)
let imageRect = CGRect.init(x: borderWith, y: borderWith, width: w-CGFloat(2)*borderWith, height: w-CGFloat(2)*borderWith)
if self.cornerRadius < CGFloat(0) {
cornerRadius = CGFloat(0)
}else if cornerRadius > min(w, h) {
cornerRadius = min(w,h)
}
let image = self.image
//绘制边框
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let bigPath = UIBezierPath.init(roundedRect: bigRect, cornerRadius: cornerRadius)
borderColor.setFill()
bigPath.fill()
//绘制图片
UIBezierPath.init(roundedRect: imageRect, cornerRadius: cornerRadius-borderWith).addClip()
image?.draw(in: bigRect)
let roundImage = UIGraphicsGetImageFromCurrentImageContext()
self.image = roundImage
UIGraphicsEndImageContext()
}
}
8、图片混合模式
self.imageView.image = [self drawPiucureFrontImage:[UIImage imageNamed:@"whiteProduct"] backImage:[UIImage imageNamed:@"gray"] blendMode:kCGBlendModeDarken alpha:1];
- (UIImage *)drawPiucureFrontImage:(UIImage *)personImage backImage:(UIImage *)hatImage blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha
{
CGSize newSize =[personImage size];
UIGraphicsBeginImageContext(newSize);
[personImage drawInRect:CGRectMake(0,0,newSize.width,newSize.height) blendMode:kCGBlendModeNormal alpha:1];
[hatImage drawInRect:CGRectMake(0,0,newSize.width,newSize.height) blendMode:blendMode alpha:alpha];
UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = newImage;
return newImage;
}
这个是混合模式的效果 在ps中是正常,以下是常用的模式
kCGBlendModeNormal --ok 正常,
kCGBlendModeMultiply, 正片叠底
kCGBlendModeScreen, 滤色
kCGBlendModeOverlay, 叠加
kCGBlendModeDarken, 变暗
kCGBlendModeLighten, 变亮
kCGBlendModeColorDodge, 颜色减淡
kCGBlendModeColorBurn, 颜色加深
kCGBlendModeSoftLight, 柔光
kCGBlendModeHardLight, 强光
kCGBlendModeDifference, 差值
kCGBlendModeExclusion, 排除
kCGBlendModeHue, 色相
kCGBlendModeSaturation, 保护度
kCGBlendModeColor, 颜色
kCGBlendModeLuminosity 明度,
这几个可以试试,每一种图片的模式都不一样。其中正片叠底 和叠加应该是我经常用到的混合模式。
9、swift中的日志打印
//封装的日志输出功能(T表示不指定日志信息参数类型)
func scxPrint(_ message:T, file:String = #file, function:String = #function,line:Int = #line) {
#if DEBUG
//获取文件名
let fileName = (file as NSString).lastPathComponent
//打印日志内容
print("\(fileName):\(line) \(function) | \(message)")
#endif
}
推荐第三方的框架CocoaLumberjack,非常强大
10、查看手机沙盒中的文件
- 方法一、mac连上你的设备,xcode中windows-》devices,选择你的设备,右边installed apps,选择你的APP,download container 下载的文件,显示包内容,即是沙盒内的文件s ;
- 方法二、获取到文件的路径,然后使用string的方法获取文件内容:
do {
try str = NSString(contentsOfFile: cacheLogsPaht, encoding: String.Encoding.utf8.rawValue)
} catch let error {
print(error)
}
- 方法三、可以使用各种调试的小插件来读取沙盒内容,例如FLEX
11、xib试用autoLayout的cell自动计算行高失效的问题,需要在viewDidLoad中加上下面两行代码
//加上这两句代码xib中的cell的高度才会自动计算
tableView?.rowHeight = UITableViewAutomaticDimension;
tableView?.estimatedRowHeight = 500;
12、tableViewCell中有UITextview,随着UITextview的编辑动态更改行高
计算出新的行高,然后调用
tableView.beginUpdates()
tableView.endUpdates()
调用后,tableview会重新刷新所有cell的行高,但是不会调用cell(for:IndexPath)代理方法来重新布局cell。这样的话textView就不会失去焦点;
如果换成是调用
reloadRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation)
tableView就会调用cell(for:IndexPath)代理方法重新创建一个新的cell来取代当前位置的cell,这样的话textview肯定会失去焦点。
具体的使用参考http://www.cocoachina.com/ios/20141226/10778.html
13、常见的循环引用
- 闭包,对象持有闭包,闭包中有包含该对象。解决方法:[weak self]in 。但是有时候需要在闭包中调用self的方法或者属性,此时不希望self已经释放掉了,可以在闭包中使用如下:
if let strongSelf = self {
strongSelf.doSomething()
}
GCD以及UIView的动画闭包不需要使用[weak self],因为self没有持有闭包,所以不会引起循环引用;
- 两个对象互相作为对方的属性。解决方法:当在其中一个类中声明另外一个对象作为属性时,使用weak修饰。
14、UIGraphicsGetCurrentContext() 获取到上下文为nil
在调用 drawRect前,视图对象会先往栈上压入一个有效的上下文并使它成为当前上下文。所以在drawRect方法中可以通过UIGraphicsGetCurrentContext()获取到上下文。但是在其他地方可能会获取到为nil,需要自己创建一个上下文:
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0);
CGContextRef context =UIGraphicsGetCurrentContext();
//然后在context上绘画。。。
UIImage *temp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
15、XCode9编码的过程中一直buiding
问题:升级Xcode9后,在编码过程中不停的buid,搞的Xcode卡出翔了。定位到是 :Storyboard里开启了自动刷新,并且你在某个视图中使用了IB_DESIGNABLE特性.
解决方法:在xcode的Editor中关闭自动刷新,详细看下图
16、iphoneX上默认视图布局都是在安全区域中布局的,因为在xib中视图的边缘设置约束的时候默认是设置距离Top Layout Guide的距离。如果想要从最顶部状态栏的上边缘开始布局,就要删除原来的上边缘Top Layout Guide的距离的约束,改为相对于superView的约束。
17、监听tableView刷新完成状态
- 方法一
tableView.reloadData()
tableView.layoutIfNeeded()
- 方法二
tableView.reloadData()
DispatchQueue.main.async {
//do something
}
第一种方式是刷新的时候强制tableView 立即刷新,第二种是获取主线程队列,然后当tableView刷新完成之后会调用这个GCD的block。
18、webView底部出现黑框
解决方法:
webView.isOpaque = false
webView.backgroundColor = UIColor.clear
19、无导航栏push到有导航栏界面
第一步:创建BaseNavigationController,重写push方法
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
super.pushViewController(viewController, animated: animated)
setNavigationBarHidden(false, animated: true)
}
第二步:在需要隐藏导航栏的视图控制器中设置如下:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
20、UITableView的estimatedRowHeight机制的坑:
iOS8以后,tableView可以自动计算行高,只需要将cell使用autoLayout布局好,设置估算行高:
tableView?.rowHeight = UITableViewAutomaticDimension
tableView?.estimatedRowHeight = 150
这样设置以后,系统就会自动计算行高,使用非常方便。但是使用过程中也遇到了一些坑:
1、有时候刷新某个cell的时候,发现tableView会上下的滚动,改变了contentOffset;
2、reloadtable后,调用tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)时,在某些情况下,table总是无法滚动到正确的指定位置,对显示效果产生了很大影响。
解决方法:
一、尽量精确的设置estimatedRowHeight,使用以下代理方法对不同的cell分别设置
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
二、不使用自动计算行高,改为手动计算;
21、swift中String的count和OC中NSString的length的区别
swift中String的count,一个字母,一个汉字,一个emoji表情的count都是1;
但是OC中的NSString的length 表示的字符个数,一个字母和一个汉字都是一个字符,但是一个emoji表情可能占用2个或者多个字符。
NSRange中使用到的就是OC中的length,如果在swift编程中包含emoji的String在使用NSRange的时候使用到了count,就会出问题了。
22、侧滑返回手势的坑
iOS 系统自带侧滑返回的功能,但是如果我们自定义导航栏的返回按钮后,侧滑返回就会失效。解决方法是可以在BaseViewController中加入以下代码:
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
self.navigationController?.interactivePopGestureRecognizer?.delegate = self as? UIGestureRecognizerDelegate
上面两行代码可能会导致navigationController的rootViewController也能够接受右滑pop事件,导致界面卡死无法push到下一个界面的情况。所以需要判断当界面为RootViewController的时候需要设置 interactivePopGestureRecognizer?.isEnabled=false。
同时可能项目中有些界面是需要禁止返回手势的,所以最终确定的方案为:在BaseNavigationController中设置delegate = self,然后实现以下代码方法:
//需要禁止侧滑返回手势的界面
fileprivate let forbidSideBackVcs: [AnyClass] = [PaySuccessViewController.self,
MakeOrderFailureViewController.self,
ToPayViewController.self,
BuyoutPayViewController.self
]
//控制返回手势是否可以用
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
var forbid = false
for aClass in forbidSideBackVcs {
if viewController.isKind(of: aClass) {
forbid = true
break
}
}
if forbid == true { //需要禁止返回手势的视图
interactivePopGestureRecognizer?.isEnabled = false
interactivePopGestureRecognizer?.delegate = nil
}else if viewControllers.first == viewController { //navigation的RootViewController需要禁止,否则在该界面侧滑后,然后再push到其他界面就会出现卡死的情况
interactivePopGestureRecognizer?.isEnabled = false
interactivePopGestureRecognizer?.delegate = nil
}else {
interactivePopGestureRecognizer?.isEnabled = true
interactivePopGestureRecognizer?.delegate = viewController as? UIGestureRecognizerDelegate
}
}
23、图片的拉伸
我们把左边10个像素和右边的40个像素给保持原样,中间的进行拉伸
let imageView = UIImageView(frame: CGRectMake(40, 40, 300, 30))
let image = UIImage(named: "test")?.resizableImageWithCapInsets(UIEdgeInsetsMake(0, 10, 0, 40), resizingMode: UIImageResizingMode.Stretch)
imageView.image = image
view.addSubview(imageView)
24、UITableView的头视图上的子视图在tableView滚动过程中和tableView不同步的问题,如下图
是因为xib中SafeArea的问题,此时有问题子视图的位置布局是相对于SafeArea布局的,只需要改为相对于SuperView布局就可以了。操作如下图,将父视图的Safe Area Layout Guade选项取消勾选就可以了。
25、swift中数字计算中出现nan和inf
infinity 代表无穷,它是类似 1.0 / 0.0 这样的数学表达式的结果。
NaN,它是 “Not a Number” 的简写,可以用来表示某些未被定义的或者出现了错误的运算,比如下面的操作都会产生 NaN:
let a = 0.0 / 0.0
let b = sqrt(-1.0)
let c = 0.0 * Double.infinity
与 NaN 再进行的运算结果也都将是 NaN,可以使用 Double 的 isNaN 或者 Darwin 中的 isnan 来判断是否为NaN。
let num = Double.NaN
if num.isNaN {
print("NaN")
}
if isnan(num) {
print("NaN")
}
// 输出:
// NaN
// NaN
26、UITableView的父视图添加了点击手势后,点击cell竟然不会调用didSelect代理方法了。原因和解决方法参考以下链接:
https://www.jianshu.com/p/53e03e558cbd
27、AVPlayer播放视频的时候,出现AVPlayerStatus.failed的情况,而且AVPlayer.error为nil。原因是创建的AVPlayer太多了,最多只能创建18个,超过18就会出现这个播放失败情况。
28、swift标识弃用的方法:
方法已经弃用
@available(*, deprecated, message: "Parse your data by hand instead")
func parseData() { }
使用该方法的时候,会报警警告,并且提示message信息。
如果是方法已经重新命名了,可以这样:
@available(*, deprecated, renamed: "loadData")
func fetchData() { }
这样使用的时候,会提示去自动修复它,如果你点击修复的话,就会自动更改名字为loadData;
如果不想让该方法被使用了,而不是使用的时候爆出警告,可以这样
@available(swift, obsoleted: 4.1, renamed: "attemptConnection")
func testConnection() { }
这样使用的时候会直接报错。
当然也可以组合起来使用:
@available(swift, deprecated: 4.0, obsoleted: 5.0, message: "This will be removed in v5.0; please migrate to a different API.")
29、app瘦身之静态库,使用的第三方的静态库(包括手动加入的.a文件以及cathage引入的frame),调试时候使用的是模拟器真机都可以使用的合成版本,发布的时候只需要使用真机的版本就可以了。分离出真机版本的命令如下:
lipo -thin arm64 ESPullToRefresh -output ESPullToRefresh.arm64
lipo -thin armv7 ESPullToRefresh -output ESPullToRefresh.armv7
lipo -create ESPullToRefresh.arm64 ESPullToRefresh.armv7 -output ESPullToRefresh.device
30、项目支持carthage集成方式的注意事项:
、使用carthage封装自己的私有库A的时候,framework A可能会用到另外的framework B。carthage是支持嵌套依赖的,但是要保证framework A的carthage文件位于工程A的git仓库的主目录下。并且需要在我们的主工程中carthage文件中列出嵌套的依赖库(也就是framework B),不管主工程有没有使用到frameworkB。这样的话我们在主工程中使用到framework A的时候,主工程会下checkout下来frameworkB,然后在checkout frameworkA, 将frameworkA的工程中carthage文件中的依赖指向主工程中的carthage文件中的frameworkB。这样所有的依赖以及嵌套的依赖都会在主工程中的carthage文件中。
、添加这个 Run Script 的作用是为了让运行时能够找到这个动态库。这点 Carthage 官方文档中没有太明确的说明。我实际的实验过,如果不添加这个 copy-frameworks 脚本,那么项目在运行的时候会因为找不到这个动态库而在启动的时候崩溃。
、frameworkA不需要添加Run Script Phases1;
31、TextKit简介
TextKit是基于CoreText的封装,系统的UITextFiled、UILabel、UITextView都是基于TextKit来实现的。
TextKit框架主要成员对象:
、NSTextStorage:NSMutableAttributedString 的子类,负责存储需要处理的文本及其属性。
、NSTextContainer:描述了一个显示区域,默认是矩形,其子类可以定义任意的形状。它不仅定义了可填充的区域,而且内部还定义了一个不可填充区域(Bezier Path 数组)。
、NSLayoutManager:负责将 NSTextStorage 中的文本数据渲染到显示区域上,负责字符的编码和布局。
、UITextView:对应要渲染的视图。
31、UIWebView设置UserAgent。
H5可以根据userAgent判断当前的环境,判断是否处于App内。所以经常我们都会自定义webView的userAgent。UIWebView设置UserAgent的方式:
UserDefaults.standard.register(defaults: customeUserAgentDic)
UserDefaults.standard.synchronize()
这样设置以后,整个app全局都会生效。当使用到其他三方库中含有的webView时,可能会有影响。
trytry中使用阿里的人脸识别的时候,经常会出现其中的H5界面报错,一开始以为是三方库的错误。后来发现只要app内进入过webView就会报错,后来才发现是自定义UserAgent的原因。
如果是使用WKWebView的话,可以直接设置webView的customUserAgent属性,不会影响到其他的webView。
32、wkwebview加载本地html时,H5获取不到本地的js文件,导致白屏。
使用wkwebview加载本地js时,不要直接使用loadRequest,而应该使用:
loadFileURL(_ URL: URL, allowingReadAccessTo readAccessURL: URL)
其中readAccessURL表示允许webView访问的沙盒目录,如果本地的js不在这个目录地下,那么webView就访问不到本地的js。