Swift / Objective_C / Xcode实际开发中可能遇到的小功能小技巧总结

一:Swift3.0为视图添加旋转动画,效果如下:

这里写图片描述

代码实现:

1
2
3
4
5
6
7
8
9
//创建动画
let anim = CABasicAnimation(keyPath: "transform.rotation")
//设置相关属性
anim.toValue = 2 * M_PI
anim.repeatCount = MAXFLOAT
anim.duration = 15
//完成之后不移除,testView被释放,动画随着一起删除
anim.isRemovedOnCompletion = false
testView(anim, forKey: nil)

二:解决项目中每次界面跳转隐藏TabBar的问题

思路:在UINavigationController 中重写pushViewController 方法,不必每次跳转都调用hidesBottomBarWhenPushed

1
2
3
4
5
6
7
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
//隐藏tabbar
if childViewControllers.count > 0 {
viewController.hidesBottomBarWhenPushed = true
}
super.pushViewController(viewController, animated: animated)
}

三:Swift3.0中使用NSLayoutConstraint为控件添加约束

1
2
3
4
5
6
7
8
//设置通过代码添加Constraint,否则View还是会按照以往的autoresizingMask进行计算
centerButton.translatesAutoresizingMaskIntoConstraints = false
//依次添加X,Y, W,H
view.addConstraint(NSLayoutConstraint(item: centerButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 0))
view.addConstraint(NSLayoutConstraint(item: centerButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: -60))
view.addConstraint(NSLayoutConstraint(item: centerButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50))
view.addConstraint(NSLayoutConstraint(item: centerButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50))

方法解读:

在view视图中,为参数1添加约束

设置 参数1(一般为视图)参数2(坐标或宽高) 属性 参数3(大于等于小鱼) 参数4(参照视图)参数5(坐标或宽高) 属性 乘以 参数6 加上 参数7

注意:单纯设置宽高的时候,参数4传入nil,参数5传入.notAnAttribute

1
view.addConstraint(NSLayoutConstraint(item: 参数1, attribute: 参数2, relatedBy: 参数3, toItem: 参数4, attribute: 参数5, multiplier: 参数6, constant: 参数7))

四:Swift3.0通过十六进制值设置UIColor

1
2
3
4
5
6
7
8
9
10
11
extension UIColor {
class func colorWithHex(hexValue: UInt32) -> UIColor {
let r = (hexValue & 0xff0000) >> 16
let g = (hexValue & 0x00ff00) >> 8
let b = hexValue & 0x0000ff
return UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: 1.0)
}
}
//示例调用:
view.backGroundColor = UIColor.colorWithHex(hexValue: 0xff0000)

五:Swift3.0中&error的写法

1
2
var error: NSError?
context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)

六:动态设置TableView的滑动范围

1
2
3
4
5
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//一般获取最后控件的最大Y坐标,labelExplain是最后一个cell下追加的控件
self.tableView.contentSize = CGSizeMake(0,CGRectGetMaxY(self.labelExplain.frame) + 10);
}

七:clipstobounds与maskstobounds的区别

1
2
3
4
5
clipsToBounds(UIView)
是指视图上的子视图,如果超出父视图的部分就截取掉
masksToBounds(CALayer)
却是指视图的图层上的子图层,如果超出父图层的部分就截取掉

八:查看真机沙盒文件夹,查看真机崩溃日志

这里写图片描述

九:常用的路径位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
模拟器的位置:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs
文档安装位置:
/Applications/Xcode.app/Contents/Developer/Documentation/DocSets
插件保存路径:
~/Library/ApplicationSupport/Developer/Shared/Xcode/Plug-ins
自定义代码段的保存路径:
~/Library/Developer/Xcode/UserData/CodeSnippets/ //如果找不到CodeSnippets文件夹,可以自己新建一个CodeSnippets文件夹。
描述文件路径
~/Library/MobileDevice/Provisioning Profiles

十:富文本和HTML字符串互相转化

1
2
3
4
5
6
7
8
9
10
11
12
13
//富文本转html字符串
- (NSString *)attriToStrWithAttributeString:(NSAttributedString *)attributeString
{
NSDictionary *tempDic = @{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute:[NSNumber numberWithInt:NSUTF8StringEncoding]};
NSData *htmlData = [attributeString dataFromRange:NSMakeRange(0, attributeString.length) documentAttributes:tempDic error:nil];
return [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
}
//html字符串转富文本
- (NSAttributedString *)strToAttriWithString:(NSString *)htmlString
{
return [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType} documentAttributes:nil error:nil];
}

十一:Swift项目中引入Objective_C三方框架后手动桥接的方法

1>手动创建一个.h的文件,比如:Demo-Bridge.h

2>在Build Setteings 中找到 Objective_C Bridging Header 添加路径$(SRCROOT)/Demo/Demo-Bridge.h

3>包含Objective_C头文件,例如:#import "UIView+WebCache.h"

十二:UITableView多行选择修改系统默认选择样式

在自定义的cell中重写layoutSubviews

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)layoutSubviews
{
[super layoutSubviews];
// 设置UITableViewCellEditControl样式
for (UIControl *control in self.subviews) {
if ([control isMemberOfClass:NSClassFromString(@"UITableViewCellEditControl")]) {
for(UIView *view in control.subviews) {
if([view isKindOfClass: [UIImageView class]]) {
UIImageView *img = (UIImageView *)view;
if(self.selected) {
//选择状态图片
img.image= [UIImage imageNamed:@"image1"];
} else {
//未选中状态图片
img.image= [UIImage imageNamed:@"image2"];
}
}
}
}
}
}

十三:Xcode项目中一键替换项目中所有类中指定文字或代码

1>快捷键command + shift + F唤醒全局搜索并进入输入状态

2>切换FindReplace(这里也可以采用正则进行查找搜索Regular Expression)

这里写图片描述

3>输入要搜索的内容和替换结果,然后点击Replace All即可

这里写图片描述

十四:NSUserDefaults判断应用程序是否是安装完首次次启动

1
2
3
4
5
6
if (![[NSUserDefaults standardUserDefaults] valueForKey:@"FirstStart"]) {
[[NSUserDefaults standardUserDefaults] setValue:@"firstStart" forKey:@"FirstStart"];
//第一次启动,可以设置欢迎页或者设置默认语言
} else {
//非第一次启动
}

十五:Swift 设置在debug模式下打印日志,并且锁定代码位置(Objective_C打印设置)

1
2
3
4
5
6
7
8
9
10
11
// 可以把下列代码放在AppDelegate的@UIApplicationMain的上方
func DebugLog<T>(messsage : T, file : String = #file, funcName : String = #function, lineNum : Int = #line) {
#if DEBUG
let fileName = (file as NSString).lastPathComponent
print("\(fileName):(\(lineNum))-\(messsage)")
#endif
}
//使用方法
DebugLog(messsage: "test")
//输出类名 + 代码行数 + 输出信息
ViewController.swift:(37)-test

十六:修改默认开发语言(Xcode默认开发语言是英语这里写图片描述)

1>先添加英语之外的一种语言

这里写图片描述

2>Show in Finder工程文件 –> 显示包内容 –> 用文本打开project.pbxproj –> 搜索developmentRegion –> 将值改为zh-Hans

这里写图片描述

3>修改成功这里写图片描述

十七:使用runtime为分类添加属性

我们知道系统的UITableViewsectionrow属性,就是定义在NSIndexPath的分类里的

这里写图片描述

示例方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//写一个UIView的分类命名:UIView+Category
UIView+Category.h
//增加的属性
@property (nonatomic, strong) NSObject *propertyTest;
UIView+Category.m
//加入运行时头文件
#import <objc/runtime.h>
@implementation UIView (Category)
//获取关联的对象
- (NSObject *)propertyTest {
return objc_getAssociatedObject(self, @selector(propertyTest));
}
//给对象添加关联对象
- (void)setPropertyTest:(NSObject *)value {
objc_setAssociatedObject(self, @selector(propertyTest), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
此后,就可以正常访问了该属性了

十八:App迭代开发版本号的规则

0>首先我们的App第一版本首次上线, 比如以1.0.0为首次上线的版本号

1>上线后突然发现一个严重的Bug那我们就要修复更新版本, 此时我们的版本号为1.0.1, 所以说如果修复Bug或者优化功能, 我们只修改叠加第三位数字, 其他不变

2>如果有了新的需求, 在原来的基础上增加了一个新功能, 那么我们的版本号变为1.1.0, 需要清空第三位数字为0, 来叠加修改第二位数字

3>如果App需求功能大改, 更新量非常大, 那我们的版本号变为2.0.0, 需要叠加修改第一位数字, 清空其他数字为0

十九:Swift中懒加载

先说说OC中的懒加载,通常是写get方法,例如:

1
2
3
4
5
6
7
8
- (DataModel *)model
{
if (!_model) {
_model = [[DataModel alloc] init];
_model.title = @"标题";
}
return _model;
}

swift中有专门的懒加载修饰符lazy,实现如下:

1
2
3
4
5
private lazy var model: DataModel = {
let model = DataModel()
model.title = "标题"
return model
}()

二十:Swift中shouldAutorotate的重写

1
2
3
4
5
6
7
8
9
override open var shouldAutorotate: Bool {
return false / true
}
//或者
open override var shouldAutorotate: Bool {
get {
return false / true
}
}

二十一:屏幕旋转的时候状态栏显示问题

如果是在视图View中,重写layoutSubviews;如果是在ViewController中重写viewWillLayoutSubviews,Swift代码如下:

1
2
3
4
5
6
7
8
9
10
//视图控制器中
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
UIApplication.shared.isStatusBarHidden = false
}
//视图中
override func layoutSubviews() {
super.layoutSubviews()
UIApplication.shared.isStatusBarHidden = false
}

二十二:区分==,isEqual,isEqualToString

==: 比较的是内存地址

isEqual: 是 NSObject 的方法,首先都会判断指针是否相等 ,相等直接返回YES,不相等再判断是否是同类对象或非空,空或非同类对象直接返回NO,而后依次判断对象对应的属性是否相等,若均相等,返回YES

isEqualToString: 是NSString的方法,从继承关系角度来说是 isEqual的衍生方法,在都是字符串的前提下,判断字符串的内容是否相等,如果知道了两个对象都是字符串,isEqualToStringisEqual要快

二十三:修改GitHub项目显示语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
打开Terminal 进入到仓库文件夹
$:cd /Users/MacName/Desktop/Demo
创建一个`.gitattributes`的文件
$:touch .gitattributes
打开文件
$:open .gitattributes
写入如下代码,比如设置语言为Swift
*.h linguist-language=swift
*.m linguist-language=swift
重新push项目到GitHub, 完成修改

二十四:Terminal命令查看系统隐藏文件

1
2
3
4
5
6
7
显示隐藏文件:
$:defaults write com.apple.finder AppleShowAllFiles -bool true
关闭显示隐藏文件:
defaults write com.apple.finder AppleShowAllFiles -bool false
***执行命令后需要打开强制退出界面(快捷键option+command+esc),重启Finder

二十五:Masonry布局后获取Frame

需要立即更新子视图的布局后获取即可

1
[self layoutIfNeeded];

二十六:Xcode同时打开两个Simulator模拟器(做通信APP方便调试)

打开终端进到xcode路径下的Applications路径

1
$:cd /Applications/Xcode.app/Contents/Developer/Applications/

打开模拟器

1
$:open -n Simulator.app/

或者执行一个脚本也可以,创建文件xim.sh,键入以下代码

1
2
3
4
#!/bin/sh
cd /Applications/Xcode.app/Contents/Developer/Applications/
open -n Simulator.app/
1
sudo sh sim.sh

会有如下提示:

这里写图片描述

点击OK后,换一个与当前模拟器设备不同的机型

这里写图片描述

然后在Xcode中选择刚选取的机型run包即可同时打开调试

实际上多开别的APP道理也是一样的,进到APP应用目录,open -n appName.app/即可

二十七:TableView检测滑动到底部和顶部(可用于聊天界面取历史消息)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y == scrollView.contentSize.height - scrollView.frame.size.height) {
NSLog(@"滑到底部加载更多");
}
if (scrollView.contentOffset.y == 0) {
NSLog(@"滑到顶部更新");
}
}
// 另外点击状态栏会调用
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
NSLog(@"点击状态栏调用");
}

二十八:区分__weak,__unsafe_unretained,__block

1
2
3
__weak 在释放指针后能够同时将指针置为nil
__unsafe_unretained 只留下一个无效的也指针
__block 打破循环 copy副本 内部修改
Stevin wechat
扫码及时获取更多文章