TabBar设置shadowImage
不起作用问题
要同时设置
backgroundImage
和shadowImage
才行
tabBar.backgroundImage = .init()
tabBar.shadowImage = .init()
切换导航栏透明效果
func updateNavBar(tintColor: UIColor?, barColor: UIColor?, isTranslucent: Bool) {
let bar = navigationController?.navigationBar
bar?.tintColor = tintColor
bar?.setBackgroundImage(barColor?.renderToImage(), for: .default)
var attrs = bar?.titleTextAttributes
attrs?[NSAttributedString.Key.foregroundColor] = tintColor
bar?.titleTextAttributes = attrs
bar?.isTranslucent = isTranslucent
bar?.barTintColor = barColor
}
设置导航栏translucent
跳动问题
/// VC里设置
extendedLayoutIncludesOpaqueBars = true
edgesForExtendedLayout = [.all]
HandyJSON deserialize(from:designatedPath:)
失败
extension String: _BuiltInBasicType {
static func _transform(from object: Any) -> String? {
switch object {
case let str as String:
return str
case let num as NSNumber:
// Boolean Type Inside
if NSStringFromClass(type(of: num)) == "__NSCFBoolean" {
if num.boolValue {
return "true"
} else {
return "false"
}
}
return formatter.string(from: num)
case _ as NSNull:
return nil
case _ as NSObject:
do {
let data = try JSONSerialization.data(withJSONObject: object, options: .fragmentsAllowed)
return String(data: data, encoding: .utf8)
}catch {
fatalError(error.localizedDescription)
}
default:
fatalError("HandyJSON 数据解析失败,请检查数据格式!")
}
}
func _plainValue() -> Any? {
return self
}
}
Collection View头部悬停
flowLayout.sectionHeadersPinToVisibleBounds = true
flowLayout.sectionFootersPinToVisibleBounds = true
Swift方法OC指定
@objc(registerClass:forCellWithReuseIdentifier:)
open func register(_ cellClass: Swift.AnyClass?, forCellWithReuseIdentifier identifier: String) {
self.collectionView.register(cellClass, forCellWithReuseIdentifier: identifier)
}
Xib构建视图
#if TARGET_INTERFACE_BUILDER
open override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.contentView.layer.borderWidth = 1
self.contentView.layer.cornerRadius = 5
self.contentView.layer.masksToBounds = true
self.contentView.frame = self.bounds
let label = UILabel(frame: self.contentView.bounds)
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 25)
label.text = "FSPagerView"
self.contentView.addSubview(label)
}
#endif
更改Info.plist,报错:Multiple commands produce...
BuildPhases->Copy Bundle Resources-> 删除Info.plist文件即可
prefersStatusBarHidden
问题
在iOS7中,实际上有一个称为modalPresentationCapturesStatusBarAppearance的UIViewController的新属性,Default value is NO.
When you present a view controller by calling the presentViewController:animated:completion: method, status bar appearance control is transferred from the presenting to the presented view controller only if the presented controller’s modalPresentationStyle value is UIModalPresentationFullScreen. By setting this property to YES, you specify the presented view controller controls status bar appearance, even though presented non–fullscreen.
The system ignores this property’s value for a view controller presented fullscreen.
Swift图片尺寸适配
extension CGSize {
func fitMaxSize(_ maxSize: CGSize = .init(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2))->CGSize {
var w = width
var h = height
let ratio = w / h
let maxW = maxSize.width
let maxH = maxSize.height
if w > maxW {
w = maxW
h = w / ratio
}
if h > maxH {
h = maxH
w = h * ratio
}
/// 如果上述缩放后还是有任一边超过最大值,继续缩放
if w > maxW || h > maxH {
return fitMaxSize()
}
return .init(width: w, height: h)
}
}
每次编译修改build号
#!/bin/bash
buildNumber=$(date +%Y%m%d.%H%M)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
真机调试报错libclang_rt.asan_ios_dynamic.dylib __asan::AsanDie:
Xcode里alt复制文件
注意在xcode里工程目录里如果alt拖动复制的文件如xib等,默认是没有勾选进target的,所以需要手动勾选,否则会出现xib未被load的情况,导致cell的register和dequeue失败。
github代理设置
open ~/.gitglobalconfig,添加:
[http]
proxy = 127.0.0.1:7890(端口号不同的代理软件可能不一样)
git终端代理设置
export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
然后开启VPN,再执行git clone就会非常快
Flutter SDK环境变量设置
export PATH=$PATH:/Volumes/Document/Flutter/SDK/flutter/bin
- 注意路径不能有"号
CodeSnippets路径
~/Library/Developer/Xcode/UserData/CodeSnippets
UIScrollView实时滚动方向判断
/// 这里存储上一次偏移量
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
self.lastScrollOffsetY = scrollView.contentOffset.y;
}
/// 这里进行实时滚动方向判断
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat y = scrollView.contentOffset.y;
CGFloat Y = self.lastScrollOffsetY;
BOOL isScrollDown = y-Y < 0;
}
NSDictionary全类型
/// __NSDictionaryI 有初始值的
NSDictionary *d1 = @{@"name": @"1", @"age": NSNull.null};
NSDictionary *d6 = [NSDictionary dictionaryWithObjectsAndKeys:@1,@"2",@2,@"",nil];
/// __NSDictionary0 空字典
NSDictionary *d2 = [NSDictionary new];
NSDictionary *d3 = [NSDictionary dictionary];
NSDictionary *d4 = [NSDictionary dictionaryWithDictionary:@{}];
NSDictionary *d7 = @{}.copy;
/// __NSSingleEntryDictionaryI 单容量
NSDictionary *d5 = [NSDictionary dictionaryWithObject:@1 forKey:@"2"];
/// __NSArrayM 可变字典
NSDictionary *d8 = [NSMutableArray new];
/// __NSFrozenDictionaryM
Charles抓包https显示unknown
1、手机浏览器输入chls.pro/ssl安装证书(通用->描述文文件与设备管理->选中Charles证书安装)。
2、安装好之后要设置信任该证书(通用->关于本机->设置信任该证书)。
获取启动storyboard的入口vc
id name = NSBundle.mainBundle.infoDictionary[@"UILaunchStoryboardName"];
UIStoryboard *sd = [UIStoryboard storyboardWithName:name bundle:nil];
UIViewController *vc = sd.instantiateInitialViewController;
OC runtime方法交换的正确用法
/// 方法交换
+ (void)zhlxib_swizzleMethod:(SEL)sel1 method2:(SEL)sel2{
Method m1 = class_getInstanceMethod(self, sel1);
Method m2 = class_getInstanceMethod(self, sel2);
if (m1 && m2) {
BOOL m1Added = class_addMethod(self, sel1, method_getImplementation(m2), method_getTypeEncoding(m2));
if (m1Added) {
class_replaceMethod(self, sel2, method_getImplementation(m1), method_getTypeEncoding(m1));
}
else{
method_exchangeImplementations(m1, m2);
}
}
}
iOS转屏
-
modal模式只支持fullscreen
- 设置shouldAutoRotate为YES
- 设置supportedInterfaceOrientations
- 设置preferredInterfaceOrientationForPresentation
-
push模式只能通过强制转屏来实现(设置UIDevice的orientation)
- 这种情况只能present出来一个nav的vc,然后在vc设置横屏并且全屏
DoKit
https://www.dokit.cn/#/index/home
Lookin
https://lookin.work/
EchoSDK
https://github.com/didi/echo
关于布局priority
需要注意的是,只能修改可选约束的优先级,也就是说:
- 不允许将优先级由小于1000的值改为1000
- 不允许将优先级由1000修改为小于1000的值
如果将优先级由250修改为1000,则会抛出异常,所以要做布局的切换时,只能从high和low之间设置;
NSUserDefaults.standardUserDefaults().registerDefaults(["maxCount": 3])
设置key不存在时,返回的默认值,但registerDefaults 设置的默认值是不会持久化存储的
参考
iOS使用.svg 参考
Xcode默认不支持.svg文件,若将文件拖入Assets.xcassets则Xcode自动将该文件当成SymbolImage处理,变成.symbolset,但可以通过修改Contents.json文件取巧
// 增加properties属性
{
"images" : [
{
"filename" : "menu.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
代码使用
UIImage *img = [UIImage imageNamed:@"menu"];
CGFloat ration = MIN(img.size.height/img.size.width, img.size.width/img.size.height);
CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height*ration);
UIImageView *iv = [[UIImageView alloc] initWithFrame:rect];
iv.backgroundColor = UIColor.greenColor;
iv.image = [img imageWithTintColor:UIColor.redColor];
[self.view addSubview:iv];
NSString中截取数字的2种方法
- 官方方法,NSScanner,但有点绕
NSMutableString *noStr = [NSMutableString new];
NSString *tmpStr;
NSScanner *sc = [NSScanner scannerWithString:appNo1];
while (!sc.isAtEnd) {
/// 丢弃数字之前的
[sc scanUpToCharactersFromSet:NSCharacterSet.decimalDigitCharacterSet intoString:NULL];
/// 保留数字部分
[sc scanCharactersFromSet:NSCharacterSet.decimalDigitCharacterSet intoString:&tmpStr];
[noStr appendString:tmpStr];
tmpStr = @"";
}
- 循环判断截取,简单容易理解
NSMutableString *noStr = [NSMutableString new];
for (int i=0; i
UISlider拖动事件监听
[self.slider addTarget:self action:@selector(sliderValueChanged:event:) forControlEvents:UIControlEventValueChanged];
- (void)sliderValueChanged:(UISlider *)sender event:(UIEvent *)event{
UITouch *touch = event.allTouches.anyObject;
self.isDragging = YES;
switch (touch.phase) {
case UITouchPhaseBegan:
self.pausePlayBlock ? self.pausePlayBlock() : nil;
break;
case UITouchPhaseMoved:
self.adjustProgressBlock ? self.adjustProgressBlock(self.slider.value) : nil;
break;
case UITouchPhaseEnded:
case UITouchPhaseCancelled:
self.playPlayBlock ? self.playPlayBlock() : nil;
self.isDragging = NO;
break;
default:
break;
}
}
UIStackView布局
- 先指定sv的frame布局,sv内部会自动布局所有subviews,自动设置subview的frame布局(自动伸缩布局),如果你先自定义subview内部subviews的布局,就必须再包裹一层uiview进行布局;
- 如果你不指定sv的frame宽高,则sv会根据它的subviews的宽高计算出它自己的宽高,这时候要求subview的宽高是明确的可以计算出来的;
- UIStackView只参与布局的计算,不会被渲染显示出来;
数组越界runtime处理
//
// NSArray+ZHL.m
#import
#import "NSArray+ZHL.h"
@implementation NSArray (ZHL)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
id list = @[
@{
@"class": @"__NSArray0",
@"selector_suffix": @"0"
},
@{
@"class": @"__NSArrayI",
@"selector_suffix": @"I"
},
@{
@"class": @"__NSSingleObjectArrayI",
@"selector_suffix": @"SingleObject"
},
@{
@"class": @"__NSArrayM",
@"selector_suffix": @"M"
},
@{
@"class": @"__NSFrozenArrayM",
@"selector_suffix": @"FrozenM"
},
];
for (id item in list)
{
id sel1 = @"objectAtIndex";
id sel2 = @"objectAtIndexedSubscript";
Class class = NSClassFromString(item[@"class"]);
id suffix = item[@"selector_suffix"];
SEL SEL1 = NSSelectorFromString([NSString stringWithFormat:@"%@:",sel1]);
SEL _SEL1 = NSSelectorFromString([NSString stringWithFormat:@"zhl_%@%@:",sel1,suffix]);
SEL SEL2 = NSSelectorFromString([NSString stringWithFormat:@"%@:",sel2]);
SEL _SEL2 = NSSelectorFromString([NSString stringWithFormat:@"zhl_%@%@:",sel2,suffix]);
[self exchangeSelector1:SEL1 class1:class selector2:_SEL1 class2:self];
[self exchangeSelector1:SEL2 class1:class selector2:_SEL2 class2:self];
}
});
}
#pragma mark 方法交换
+ (void)exchangeSelector1:(SEL)sel1 class1:(Class)cls1 selector2:(SEL)sel2 class2:(Class)cls2{
Method m1 = class_getInstanceMethod(cls1, sel1);
Method m2 = class_getInstanceMethod(cls2, sel2);
if (m1 && m2) {
method_exchangeImplementations(m1, m2);
}
}
#pragma mark - index方法
- (id)zhl_objectAtIndexFrozenM:(NSUInteger)index{
NSAssert(index
Pods中使用Swift和Objective-C混编-编译不通过的原因-ld: symbol(s) not found for architecture arm64
原因:一般人在Objective-C项目和Swift混编时,会创建桥接文件:项目名-Bridging-Header.h .
在Objective-C项目中新建Swift文件时会自动提示是否创建桥接文件,点击蓝色按钮同意就行了.
而出现这个问题的原因是,桥接文件创建成功之后我把我创建的Swift文件给删除了(因为个人认为用不到),如是就提示上面的警告,并且编译不通过。
解决:在项目中新增一个.swift文件即可
自定义NavigationController
let nav = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)
nav.pushViewController(FeedViewController(), animated: false)
class CustomNavigationBar: UINavigationBar {
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "MARSLINK"
label.font = AppFont()
label.textAlignment = .center
label.textColor = .white
return label
}()
let statusLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "RECEIVING"
label.font = AppFont(size: 13)
label.textAlignment = .center
label.textColor = UIColor(hex6: 0x42c84b)
label.sizeToFit()
return label
}()
let statusIndicator: CAShapeLayer = {
let layer = CAShapeLayer()
layer.strokeColor = UIColor.white.cgColor
layer.lineWidth = 1
layer.fillColor = UIColor.black.cgColor
let size: CGFloat = 8
let frame = CGRect(x: 0, y: 0, width: size, height: size)
layer.path = UIBezierPath(roundedRect: frame, cornerRadius: size / 2).cgPath
layer.frame = frame
return layer
}()
let highlightLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.fillColor = UIColor(hex6: 0x76879D).cgColor
return layer
}()
var statusOn = false
override init(frame: CGRect) {
super.init(frame: frame)
layer.addSublayer(highlightLayer)
layer.addSublayer(statusIndicator)
addSubview(titleLabel)
addSubview(statusLabel)
barTintColor = .black
updateStatus()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let titleWidth: CGFloat = 130
let borderHeight: CGFloat = 4
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: titleWidth, y: 0))
path.addLine(to: CGPoint(x: titleWidth, y: bounds.height - borderHeight))
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height - borderHeight))
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
path.addLine(to: CGPoint(x: 0, y: bounds.height))
path.close()
highlightLayer.path = path.cgPath
titleLabel.frame = CGRect(x: 0, y: 0, width: titleWidth, height: bounds.height)
statusLabel.frame = CGRect(
x: bounds.width - statusLabel.bounds.width - CommonInsets.right,
y: bounds.height - borderHeight - statusLabel.bounds.height - 6,
width: statusLabel.bounds.width,
height: statusLabel.bounds.height
)
statusIndicator.position = CGPoint(x: statusLabel.center.x - 50, y: statusLabel.center.y - 1)
}
func updateStatus() {
statusOn.toggle()
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
statusIndicator.fillColor = (statusOn ? UIColor.white : UIColor.black).cgColor
CATransaction.commit()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6) {
self.updateStatus()
}
}
}
文本高度计算并缓存
public struct TextSize {
private struct CacheEntry: Hashable, Equatable {
let text: String
let font: UIFont
let width: CGFloat
let insets: UIEdgeInsets
func hash(into hasher: inout Hasher) {
hasher.combine(text)
hasher.combine(width)
hasher.combine(insets.top)
hasher.combine(insets.left)
hasher.combine(insets.bottom)
hasher.combine(insets.right)
}
static func ==(lhs: TextSize.CacheEntry, rhs: TextSize.CacheEntry) -> Bool {
return lhs.width == rhs.width && lhs.insets == rhs.insets && lhs.text == rhs.text
}
}
private static var cache: [CacheEntry: CGRect] = [:] {
didSet {
assert(Thread.isMainThread)
}
}
public static func size(_ text: String, font: UIFont, width: CGFloat, insets: UIEdgeInsets = .zero) -> CGRect {
let key = CacheEntry(text: text, font: font, width: width, insets: insets)
if let hit = cache[key] {
return hit
}
let constrainedSize = CGSize(width: width - insets.left - insets.right, height: .greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: font]
let options: NSStringDrawingOptions = [.usesFontLeading, .usesLineFragmentOrigin]
var bounds = (text as NSString).boundingRect(with: constrainedSize, options: options, attributes: attributes, context: nil)
bounds.size.width = width
bounds.size.height = ceil(bounds.height + insets.top + insets.bottom)
cache[key] = bounds
return bounds
}
}
快捷转换hex6颜色到RGBA
/// hex6 color
UIColor *_HEXColor(UInt32 hex6){
CGFloat divisor = 255;
CGFloat red = (CGFloat)((hex6 & 0xFF0000)>>16) / divisor;
CGFloat green = (CGFloat)((hex6 & 0x00FF00)>>8) / divisor;
CGFloat blue = (CGFloat)(hex6 & 0x0000FF) / divisor;
return [UIColor colorWithRed:red green:green blue:blue alpha:1];
}
第一次安装CocoaPods后拉去Specs慢的问题
用lantern开启代理,然后执行pod setup大概2个小时左右就能clone完成;
这个问题主要在每次pod版本升级后都会遇到,同样的方法;
查看specs的大小https://api.github.com/repos/CocoaPods/Specs
iOS禁用黑暗模式
pod repo注意
当你在Podfile里指定
source '[email protected]:flytoo/PTSpecs.git
,执行pod install
时,pod会自动同步指定的仓库索引到/Users/zhuxuhong/.cocoapods/repos/gitee-ptspecs
文件夹;但是如你在pod repo push PTSpecs
时指定的不是gitee-ptspecs
,那么在你执行pod install
时,就会报错误Unable to find a specification for xxx
。
正确顺序应该为
pod repo add PTSpecs [email protected]:flytoo/PTSpecs.git
pod repo push PTSpecs *.podspec --allow-warnings --verbose
pod repo update gitee-ptspecs
pod update --no-repo-update --verbose
.podspec文件路径注意
s.source_files = 'DemoPodLibs/Classes/Sub1/**/*'
或者
s.source_files = 'DemoPodLibs/Classes/Sub1/*.{h,m}'
Debug模式下无法断点调试Pods库
将JSON对象中的null替换为@{}
使用
id str = @"{\"status\":0, \"data\":\{\"list\": [], \"user\": null }, \"msg\": \"\"}";
id json = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
/// 这样取值会崩溃,因为null对象不等于nil指针,null对象无法调用方法(NSNull.null为NSNull的一个单例,并没有下标方法,它是一个对象,而非nil指针)
NSLog(@"%@",json[@"data"][@"user"][@"name"]);
/// 这样不会崩溃,因为nil取值(调用下标方法无效但不崩溃)
NSString *userName = [JSONTool nullsReplaced:json][@"data"][@"user"][@"name"];
NSLog(@"after: %d",(userName == nil));
代码
+ (id)nullsReplaced: (id)object{
if ([object isKindOfClass:[NSDictionary class]]){
return [self nullDic:object];
}
else if([object isKindOfClass:[NSArray class]]){
return [self nullArr:object];
}
else if([object isKindOfClass:[NSString class]]){
return [self stringToString:object];
}
else if([object isKindOfClass:[NSNull class]]){
return [self nullToDictionary];
}
return object;
}
/// 处理字典类型
+ (NSDictionary *)nullDic: (NSDictionary *)myDic{
NSArray *keyArr = [myDic allKeys];
NSMutableDictionary *resDic = [[NSMutableDictionary alloc]init];
for (int i = 0; i < keyArr.count; i ++){
id obj = [myDic objectForKey:keyArr[i]];
obj = [self nullsReplaced:obj];
[resDic setObject:obj forKey:keyArr[i]];
}
return resDic;
}
/// 处理数组类型
+ (NSArray *)nullArr: (NSArray *)myArr{
NSMutableArray *resArr = [[NSMutableArray alloc] init];
for (int i = 0; i < myArr.count; i ++){
id obj = myArr[i];
obj = [self nullsReplaced:obj];
[resArr addObject:obj];
}
return resArr;
}
+ (NSString *)stringToString:(NSString *)string{
return string;
}
/// 将`null`类型的项目转化成 @{}
+ (NSDictionary *)nullToDictionary{
return @{};
}
柔和的弹跳缩放动画
self.wrapperView.transform = CGAffineTransformMakeScale(0.3, 0.3);
[UIView animateWithDuration:0.65 delay:0 usingSpringWithDamping:0.65 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.wrapperView.alpha = 1;
self.wrapperView.transform = CGAffineTransformMakeScale(1, 1);
} completion:nil];
NSPredicate使用
_goodsList = @[
@{
@"status": @1
},@{
@"status": @2
}
];
id pre = [NSPredicate predicateWithFormat:@"self.status != '2'"];
_goodsList = [_goodsList filteredArrayUsingPredicate:pre];
github访问加速
Cocoapods master快速版本
git clone https://git.coding.net/CocoaPods/Specs.git ~/.cocoapods/repos/master
Cocoapods 安装指定版本
sudo gem install cocoapods -v 1.7.5 -n /usr/local/bin
Xcode中注释+代码分割标记
///MARK: 这是方法注释,又起到分割作用
+ (void)testFunc{
}
///MARK: - 这是方法注释,又起到带横线分割作用
setObject:forkey:与setValue:forKey:的区别
-
setObject:forkey:
中 object 是不能够为 nil 的,不然会报错。 -
setValue:forKey:
中 value 能够为 nil,但是当 value 为 nil 的时候,会自动调用removeObjectForKey:
方法。 -
setValue:forKey:
中 key 的只能是 NSString类型, -
setValue:forKey:
可以应用于任意NSObject,与setValue: forKeyPath
相似,同为为KVC方法; -
setObject:forkey:
中的 key是id类型。 -
setObject:forkey:
有相对方法removeObjectForKey:
pod lint错误unknown: Encountered an unknown error (Could not find a ios simulator (valid values:).
升级pod或卸载重装即可
// 升级
sudo brew upgrade cocoapods
// 安装指定版本
sudo gem install cocoapods --version x.x.x
// 卸载
sudo gem uninstall cocoapods
Swift4.0变化的ABI
- 重写父类中的NSObject方法时,必须要在父类方法前加上
@obj
; -
Notification.Name.UITextField.textDidChangeNotification
等系统组件通知名字相关的改为了UITextField.textDidChangeNotification
; -
String.characters.count
改为了String.count
; - 支持多行字符串定义
''' 多行 '''
,三个'号标识; -
extension
中可以调用private
方法; -
vc.childViewControllers
改为vc.children
; -
vc.removeFromParentViewController()
改为vc.removeFromParent()
; -
vc.addChildViewController(:)
改为vc.addChild(:)
; -
UIAlertControllerStyle
改为UIAlertController.Style
; -
UIApplicationLaunchOptionsKey
改为UIApplication.LaunchOptionsKey
; - 闭包中如果无参数,则由
(Void)
改为()
,调用时由{_ in}
改为{}
; -
UIFont.Weight.light
; -
NSAttributedString.Key.foregroundColor
等; -
@Inspectable
不可用;
用do{}while(0)来定义宏
Linux和其它代码库里的宏都用do/while(0)来包围执行逻辑,因为它能确保宏的行为总是相同的,而
不管在调用代码中使用了多少分号和大括号
。
#define foo(x) do{ bar(x); baz(x); } while(0)
iOS网络变化监听
// 添加观察者
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
onNotifyCallback, // callback CFSTR("com.apple.system.config.network_change"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
// 回调
static void onNotifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
NSString* notifyName = (NSString*)name;
// this check should really only be necessary if you reuse this one callback method
// for multiple Darwin notification events
if ([notifyName isEqualToString:@"com.apple.system.config.network_change"]) {
// use the Captive Network API to get more information at this point
// https://stackoverflow.com/a/4714842/119114
} else {
NSLog(@"intercepted %@", notifyName);
}
}
nullable/nonnull、__nullable/__nonnull、_Nullable/_Nonnull用法
- (nullable NSString*)method;
- (NSString* __nullable)method;
- (NSString* _Nullable)method;
- (nonnull NSString*)method;
- (NSString* __nonnull)method;
- (NSString* _Nonnull)method;
几个可以用的RTMP电视直播源
- 香港卫视,rtmp://live.hkstv.hk.lxdns.com/live/hks
- 香港财经,rtmp://202.69.69.180:443/webcast/bshdlive-pc
- 韩国GoodTV,rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp
- 韩国朝鲜日报,rtmp://live.chosun.gscdn.com/live/tvchosun1.stream
- 美国1,rtmp://ns8.indexforce.com/home/mystream
- 美国2,rtmp://media3.scctv.net/live/scctv_800
- 美国中文电视,rtmp://media3.sinovision.net:1935/live/livestream
- 湖南卫视,rtmp://58.200.131.2:1935/livetv/hunantv
http://ivi.bupt.edu.cn/
实现UITextView滚动到底部
_textView.layoutManager.allowsNonContiguousLayout = NO;
[_textView scrollRangeToVisible:NSMakeRange(_textView.text.length, 1)];
OC实现Swift Class中static var value: String?
// .h
@property(nonatomic, class, copy)NSString *value;
// .m 需实现类属性的setter和getter方法
// 定义私有静态全局变量
static NSString *_kValue;
+ (void)setValue:(NSString *)value{
_kValue = value;
}
+ (NSString *)value{
return _kValue;
}
Mac允许任何来源
程序安装
sudo spctl --master-disable
@synchronized (self){}
- 这个主要是考虑多线程的程序,这个指令可以将
{}
内的代码限制在一个线程执行,如果某个线程没有执行完,其他的线程如果需要执行就得等着。 -
@synchronized
的作用是创建一个互斥锁,保证此时没有其它线程对self
对象进行修改,作用类似于NSLock
。
NSArrayenumerateObjectsUsingBlock:
__block NSInteger index = -1;
[self.streamLines enumerateObjectsUsingBlock:^(TALStreamInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([streamInfo.lineId isEqualToString:obj.lineId]) {
index = idx;
*stop = YES;
}
}];
return index;
iOS单例方法SDK约束
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_UNAVAILABLE("allocWithZone not available, call sharedInstance instead");
+ (instancetype)alloc OBJC_UNAVAILABLE("alloc not available, call sharedInstance instead");
- (instancetype)init OBJC_UNAVAILABLE("init not available, call sharedInstance instead");
+ (instancetype)new OBJC_UNAVAILABLE("new not available, call sharedInstance instead");
- (instancetype)copy OBJC_UNAVAILABLE("copy not available, call sharedInstance instead");
防止CAAnimationDelegate设置引起的内存泄漏
// 注意delegate是strong类型的,在设置duration后动画开始执行,
// 那么对于delegate是进行了strong持有,需要注意及时self释放
@property(nullable, strong) id delegate;
防止[self performSelector:withObject:afterDelay:]
引起的内存泄漏
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
AppleDoc文档导出
- 安装
终端命令运行
git clone git://github.com/tomaz/appledoc.git
cd ./appledoc
sudo sh install-appledoc.sh
- 使用
cd 到类文件目录
appledoc --project-name "DemoDoc" --project-company "TAL.inc" --no-create-docset ./
-
效果
html
AppleDoc注释规范
/**
* @brief 这是一个AppDoc规范示例方法
* @param param1 第一个参数
* @param param2 第二个参数
* @return BOOL值
*/
- (BOOL)thisIsADemoFuncWithParam1: (NSString *)param1
pram2: (NSInteger)param2{
return NO;
}
XCode代码注释
//MARK: mark here
//TODO: todo here
//FIXME: fix me later
//???: What is this shit
//!!!: DO NOT TOUCH MY CODE
#pragma mark - 带分割线
.ips日志文件分析
- 终端命令
find /Applications/Xcode10.app -name symbolicatecrash -type f
找到symbolicatecrash
工具,选择xxx/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
这个 - 将崩溃App的
.dSYM文件
和.ips文件
拷贝到symbolicatecrash
工具同目录下 - 将
.ips文件
后缀改为.crash
- 终端运行命令
./symbolicatecrash /绝对路径/xxxx.crash /绝对路径/xxx.app.dSYM > xxx.crash
,- 如果提示错误
Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69.
, - 接着运行终端命令:
export DEVELOPER_DIR="/Applications/Xcode10.app/Contents/Developer"
(注意这个地方的Xcode要指定为xcode-select --switch的对应版本)
- 如果提示错误
Xcode显示编译用时
终端运行命令defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES
改善Swift项目编译时间
终端运行命令defaults write com.apple.dt.Xcode BuildSystemScheduleInherentlyParallelCommandsExclusively -bool NO
- 注意该方法只对Xcode9.2及以上版本生效