持续更新,不断积累... 欢迎留言!
1、status bar电池栏 强制刷新
在控制器中调用
if([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]){
[self prefersStatusBarHidden];
[self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}
2、监听设备旋转方向并且旋转视图
//添加通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil];
//开始监听
- (void)deviceOrientationDidChange
{
if([UIDevice currentDevice].orientation == UIDeviceOrientationPortrait) {
self.isRightLandscape = false;
[self orientationChange:NO];
} else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) {
self.isRightLandscape = true;
[self orientationChange:YES];
}
}
- (void)orientationChange:(BOOL)landscapeRight
{
//隐藏status bar
if([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]){
[self prefersStatusBarHidden];
[self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
if (landscapeRight) {
[UIView animateWithDuration:0.2f animations:^{
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
self.view.bounds = CGRectMake(0, 0, height, width);
}];
} else {
[UIView animateWithDuration:0.2f animations:^{
self.view.transform = CGAffineTransformMakeRotation(0);
self.view.bounds = CGRectMake(0, 0, width, height);
}];
}
}
3、折叠函数方法快捷键
command + option + left or + right = 折叠 or 展开
4、不管项目配置如何,让某个控制支持任意方向旋转。
屏幕旋转的方式大概有以下几种:
1、旋转view
2、旋转的时候跳转一个只支持横屏的控制器
3、重写appdelegate的方向支持的方法(我觉得最靠谱的一个)。
如果页面只是很简单的业务,就用第一种。
方式一: 监听设备旋转方向, 改变当前view的transform
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil];
- (void)orientationChange:(BOOL)landscapeRight
{
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
if (landscapeRight) {
[UIView animateWithDuration:0.25f animations:^{
self.view.transform = CGAffineTransformMakeRotation(-M_PI_2);
self.view.bounds = CGRectMake(0, 0, height, width);
}];
[self enterLandscapeScreen];
} else {
[UIView animateWithDuration:0.25f animations:^{
self.view.transform = CGAffineTransformIdentity;
self.view.bounds = CGRectMake(0, 0, width, height);
}];
[self exitLandscapeScreen];
}
if([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]){
[self prefersStatusBarHidden];
[self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}
}
方式二:重写以下几种方法,通过present的方式显示就可以让控制器横屏,但是如果涉及到键盘输入,就需要将键盘进行旋转
- (BOOL)shouldAutorotate {
return false;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeLeft;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft;
}
- 键盘旋转成横屏, iOS9.0以后是根据第三个remoteWindow控制键盘方向的。这里有一个小问题,就是拿不到键盘准确的高度,我尝试使用了通知和获取window中的view,都没找到准确的高度,所有我使用的是第三种横屏方式;
let windows = UIApplication.shared.windows
let deviceSize = UIScreen.main.bounds.size;
if windows.count == 3 {
let window = windows.last;
window?.bounds = CGRect.init(x: 0, y: 0, width: deviceSize.width, height: deviceSize.height)
window?.transform = CGAffineTransform.init(rotationAngle: CGFloat(Double.pi / 2.0))
}
方式三:交换delegate的supportedInterfaceOrientationsForWindow方法,切记要在离开这个页面的时候,在调用一次交换方法 ,也就是把方法 又交换回去。
- (void)setupOrientationConfig {
UIApplication *application = [UIApplication sharedApplication];
id appdelegate = application.delegate;
SEL implSelector = @selector(ncn_application:supportedInterfaceOrientationsForWindow:);
Method originalMethod = class_getInstanceMethod([appdelegate class], @selector(application:supportedInterfaceOrientationsForWindow:));
if (originalMethod == nil) {
//appdelegate必须实现 这个方法
NSAssert(0, @"appdelegate must define application:supportedInterfaceOrientationsForWindow:");
}
Method newMethod = class_getInstanceMethod(self.class, implSelector);
method_exchangeImplementations(originalMethod, newMethod);
}
- (UIInterfaceOrientationMask)ncn_application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait;
}
//手动触发 设备方向改变
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeLeft]
forKey:@"orientation"];
}
// 在横屏状态下 回到桌面再进入app,并且返回到上一页面的时候,有可能设备不会自动进行旋转,这时候可以在需要旋转的时候调用attemptRotationToDeviceOrientation
UIViewController.attemptRotationToDeviceOrientation()
5、UISegmentedControl选中背景颜色适配
if #available(iOS 13.0, *) {
self.titleSegment.selectedSegmentTintColor = .HWColorWithHexString(hex: "#639FF8")
self.titleSegment.tintColor = UIColor.white;
} else {
//setBackgroundImage 没有用
self.titleSegment.tintColor = UIColor.HWColorWithHexString(hex: "#639FF8");
};
6、一个很好用的图片编辑插件 LFMediaEditingController!!
- (void)editImage:(UIImage *)image {
LFPhotoEditingController *vc = [[LFPhotoEditingController alloc] initWithOrientation:UIInterfaceOrientationPortrait];
[vc setEditImage:image];
vc.delegate = self;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[self presentViewController:nav animated:true completion:nil];
}
7、运存app -> 内存一直增加到崩溃为止
碰到这种问题根本看不到任何错误日志,最笨的办法就是一行行代码去运行检查或者使用instruments,因为这个时候的CPU是全力运行的。我今天就碰到这样一个问题,我使用的是前者的解决办法,花了两个小时排查发现是因为给label的text赋值了一个null对象,这是因为后台接口编写不规范,需要的是个字符串,没值就返回了个null对象。当然前端也可以过滤,但是我就是不想,我很生气!
8、用UIBezierPath画带箭头的直线
3031591951212_.pic.jpg- (void)graphStrightLineArrow:(CGPoint)endpoint {
UIBezierPath *graphPath = [UIBezierPath bezierPath];
[graphPath moveToPoint:self.startPoint];
[graphPath addLineToPoint:endpoint];
CGFloat distance = 40 * self.pointpXRatio;
//弧度
CGFloat radian = (60 * M_PI) / 180;
//正切
CGFloat y1=(endpoint.y - self.startPoint.y);
CGFloat x1=(endpoint.x - self.startPoint.x);
CGFloat tangent = -atan2(y1, x1);
//右边线
CGFloat point3x = endpoint.x + distance * sin(tangent - radian);
CGFloat point3y = endpoint.y + distance * cos(tangent - radian);
[graphPath addLineToPoint:CGPointMake(point3x, point3y)];
[graphPath moveToPoint:endpoint];
//左边线
CGFloat point4x = endpoint.x + distance * sin(tangent + M_PI + radian);
CGFloat point4y = endpoint.y + distance * cos(tangent + M_PI + radian);
[graphPath addLineToPoint:CGPointMake(point4x, point4y)];
self.path = graphPath.CGPath;
}
9、去掉TabBar的黑线
试了网上的很多方案都不好使,还是自己看图层靠谱。
在TabBarVC的viwDidAppear里面 添加以下代码
for (int i = 0; i < self.tabBar.subviews.count; i++) {
UIView *view = self.tabBar.subviews[I];
NSString *classstr = NSStringFromClass(view.classForCoder);
if ([classstr isEqualToString:@"_UIBarBackground"] || view.frame.origin.y == -0.5) {
view.backgroundColor = UIColor.whiteColor;
}
}
10、Failed to connect to github.com port 443: Operation timed out 终端ping github 超时解决办法
网上试了很多办法,都不行,最后找到是由于本地DNS解析域名失败,本地有一个域名ip 地址映射表,修改为正确的ip就解决问题了。
- 查询github IP, 输入 github.com
2、打开终端,输入命令 sudo nano /private/etc/hosts 找到github.com 那行 然后修改为查询的ip。
image.png3、再次ping就通了
image.png
如果失败,多尝试几次第一步骤, 因为 github.com 域名的ip 会变。
11、本地pod混编库引用的问题
- 本地库中的swift类,在外面引用不到。
这是因为swift中的类在module中默认是受保护的,在同一个module中是共享的,但是如果想在另外一个module使用这个类,必须使用public关键字修饰。
真的要疯,这个问题困扰了我将近两天,一直以为是我podspec的设置问题,没找到解决问题的关键点;其实就是自己的swift基础太差,人太蠢。
- 在项目中使用库中的swift类中引用的一个OC类报错,该类没有这个属性
因为我在项目的OC桥接文件中引用这个库的OC相关类,这也就造成了,把这个库的所有OC类桥接到项目的module中去了,因此原库的module中就没有那些OC类了,所以才会报错。
12、获取手机连接WiFi的mac地址问题
- 12.0以后需要在证书添加wifi information支持
- 在获取的时候必须定位之后才能获取到(搞不明白为什么)
以下是获取wifi的mac地址代码,ssid是名称 bssid是Mac地址
func getWiFiBSsid() -> String? {
var bssid: String?
if let interfaces = CNCopySupportedInterfaces() as NSArray? {
for interface in interfaces {
if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? {
bssid = interfaceInfo[kCNNetworkInfoKeyBSSID as String] as? String
break
}
}
}
return bssid
}
13、Include of non-modular header inside framework module
- 两个本地库相互依赖的时候,其中一个库在.h文件中 声明另外一个库的文件报错,.m文件中引用就没有问题;
- 我查了网上很多的解决办法都不好使,最后我在添加了相关设置之后,写全了文件的路径就编译成功了,如:#import "库A/类名.h", 没有提示也没事;
14. Swift JSONDecoder 解析json为结构体失败
由于写法的不同可能导致结构体数据为空。
开发环境: swift 5.0 xcode:12.5.1
- 失败写法:
let info = try? JSONDecoder.init().decode(UserInfo.self, from: data!)
- 成功写法:
var info: UserInfo? do { info = try JSONDecoder.init().decode(UserInfo.self, from: data!) } catch { print(error) }
具体原因不明,可能因为swift语言版本原因!
15. 在swift 本地pod库中的xib中,使用bundle中的图片,显示不出来的问题
开发环境: swift 5.0 xcode:12.5.1
- 在使用OC 本地pod库,解决办法是将xib和要用到的图片放到一个bundle中,就可以解决该问题;
- 在使用swift 本地pod库,解决办法在xib中的前面加上该图在bundle中的路径,bundle也要加上。如:
ExPApp.bundle/Assets/common/subject_symbol.png
; - 在.podspec文件中,将图片和xib放到一个bundle中的写法是:
'ExPApp' => ['ExPApp/Assets',"Classes/**/*.xib",
"Classes/**/**/*.xib",
"Classes/**/**/**/*.xib",
"Classes/**/**/**/**/*.xib",]
}
但是在swift的pod库中,就算这么写。xib也不会被放到bundle中
16. Swift 中 UILabel显示html内容的写法
let html = _model.subjects ?? ""
do {
if let data = html.data(using: String.Encoding.unicode, allowLossyConversion: true) {
let attStr = try NSAttributedString.init(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html,], documentAttributes: nil)
self.subjectLabel.attributedText = attStr
}
} catch {
self.subjectLabel.text = html
}
17. 给UIButton设置图片或者UILabel设置内容导致CPU跑到100%,内存直接撑到程序崩溃?
- UIButton可能的原因,将button以addSubView的方式添加到了UIStackView中,并且设置了图片。应该使用addArrangedSubview;
- UILabel可能的原因,在OC中可能出现赋值的类型不是一个字符串,可能是其他的对象,比如从网络解析到了一个NSNull对象赋值给label的text;
18. Cocoapods 组件化(本地库)内 OC 调用swift
#import <YourSDK/YourSDK-Swift.h>
Swift 类必须声明为public
19. 执行pod init 报错:RuntimeError - [Xcodeproj] Unknown object version.
更新pod版本就行了,执行命令:sudo gem install cocoapods --pre
20. 将UIButton添加到一个UIStackView中的时候,button的size设置无效的问题;
解决方法一: 使用layout方式给button设置size
解决方法二: 重写button的intrinsicContentSize方法设置size
21. pod update 报错 Couldn't determine repo type for URL: 'xxxxxx.git'
问题背景:某个第三方库需要进行更新,而且source原地址也进行了变更。在更新时,应该是新版本的repo跟旧版本的repo冲突,造成了报错;
解决办法:删除本地的相关源的repo,然后重新pod update就ok了;
本地repo库路径: ~/.cocoapods/repos/
手动删除或者执行命令删除指定repo:pod repo remove 库名称
22. 关于结构体字节对齐的问题
背景:最近在做TCP交互,在解析包头的时候,我使用的是结构体进行定义解析的,我们定的协议头是21个字节。但是在分配内存的时候,根据字节对齐是按照结构体中类型最大的进行对齐,我这边最大的是指针4个字节。所以分配的是24个字节;
struct XXXXXXXXHeader {
/// 协议头名称
char name[8];
/// 版本号
uint8_t version;
/// 设备类型 01 == Android 02 == iOS 03 == other
uint8_t deviceType;
/// 消息类型 0x01 == 请求 0x02 == 回复 0xff == 心跳
uint8_t message_type;
/// 主业务id
uint8_t main_id;
/// 子业务id
uint8_t sub_id;
/// 消息id
uint32_t session_id;
/// 发送的数据长度
uint32_t content_length;
};
问题:在发送消息时对包头序列化时,最后一个content_length,总是解析错误,明明是0,却总是解析出一些乱七八糟的值。原因:根本问题是因为栈溢出了,在读取content_length的值,会多读取另外3个字节的地址值;
解决办法:让结构体的字节对齐以1个字节对齐,这样在给结构体分配内存的时候,只会分配21个字节。使用#pragma pack();
#pragma pack(push,1)
struct XXXXXXXXHeader {
/// 协议头名称
char name[8];
/// 版本号
uint8_t version;
/// 设备类型 01 == Android 02 == iOS 03 == other
uint8_t deviceType;
/// 消息类型 0x01 == 请求 0x02 == 回复 0xff == 心跳
uint8_t message_type;
/// 主业务id
uint8_t main_id;
/// 子业务id
uint8_t sub_id;
/// 消息id
uint32_t session_id;
/// 发送的数据长度
uint32_t content_length;
};
#pragma pack(pop)
23. perfromSelector 多参数传递
我的应用场景:单例实现多代理,在调用代理方法时,封装一个公共方法处理调用。这个方法只需要接受两个参数,一个是action:Selector,另一个是数组:数组中顺序存放action中需要传递的参数;
解决办法:通过NSInvocation处理
代码如下:
- (void)notifiyDelegateWithAction:(SEL)action withObjects:(NSArray *)objs {
dispatch_async(self.delegateQueue, ^{
for (NSObject *obj in self.delegates) {
if ([obj respondsToSelector:action] && obj) {
NSMethodSignature *signature = [obj methodSignatureForSelector:action];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:obj];
[invocation setSelector:action];
for (int i = 0; i < objs.count; i++) {
NSObject *value = objs[i];
// 因为每个方法都有两个隐式参数 一个是调用方法的对象self 一个是调用的方法名称_cmd,所以从第2个开始设置参数
[invocation setArgument:&value atIndex:i + 2];
}
// 强引用所有参数
// [invocation retainArguments];
dispatch_async(dispatch_get_main_queue(), ^{
[invocation invoke];
});
}
}
});
}
再传参数的时候有个坑 如果形参是基本数据类型(比如枚举),就只能用基本数据类型定义,再把指针传进去。参数才能正确拿到
NSObject *value = objs[i];
if ([value isKindOfClass:NSNumber.class]) {
int intValue = [(NSNumber *)value intValue];
[invocation setArgument:&intValue atIndex:i + 2];
}
24. 在CALayer 绘制图片是倒着的问题
在绘制的时候上下文的ctm默认是这样的:
a 是scaleX d是scaleY
(CGAffineTransform) $R0 = {
a = 1
b = 0
c = 0
d = -1
tx = 0
ty = 0
}
然后再调用drawImage的时候,ctm的ty会变成负数,这样就在Y轴上进行翻转了;
解决:在绘制前将ty正向偏移一个图片的高度和scaleY设置成1,就可以解决
override func draw(in ctx: CGContext) {
let image = UIImage.init(named: self.imageUrl) ?? UIImage.init(contentsOfFile: self.imageUrl)
if let cgImage = image?.cgImage {
// 默认scaleY 是-1.0
ctx.translateBy(x: 0, y: self.bounds.size.height)
ctx.scaleBy(x: 1.0, y: -1.0)
ctx.draw(cgImage, in: .init(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height));
ctx.restoreGState();
}
}
25. 组件化开发之OC与Swift混编,在swift中无法使用OC类中的成员变量或者函数;
错误信息:may not be available in this context
错误原因:在pod本地库A中用到另外一个库B的类型时,不管是用作变量还是函数的形参,在项目swift中用这个库A类的该变量或者函数,会提示找不到。因为在swift中链接不到在库A使用的库B的那个类型;总结来说就是没有穿透链接...
解决办法: 在库A中将用的库B类型涉及的那个文件设置成prefix;
network_spec.prefix_header_contents = <<-DESC
#import "AFURLRequestSerialization.h"
DESC
25. 组件化混编之在OC项目中的OC中调用本地OC库中的swift
描述: pod本地库是混编的,有OC也有swift,在项目中的OC需要用到pod本地库中的swift类;
解决办法:在OC类中需要访问swift的#import方式改为@import YourSDK.swift
,就可以访问库中所有的swift类了;
条件:pod库的swift类需要声明为public
;
环境:XCode version = 13.2 ; swift version = 5.0;
pod库的文件目录如下:
image.png
26. 代理方法不走,项目一启动,断点状态会变成无效;
如图:
image.png
原因: 因为swift版本不同的原因,可能是你从其他项目拷贝过来的代码;只要是方法的类型不一样,这个代理方法也不会走。可能断点状态的变化,算是xcode给的提示吧;
网友评论