获取app top view controller
+ (UIViewController * _Nullable)getAppTopViewController {
UIWindow *window = [self getAppKeyWindow];
if(!window) {
NSLog(@"Not found a key window for application: %@!!!", UIApplication.sharedApplication);
return nil;
}
UIViewController *vc = [window rootViewController];
while ([vc presentedViewController]) {
vc = [self _getTopViewControllerOf:vc.presentedViewController];
}
return vc;
}
+ (UIViewController * _Nullable)_getTopViewControllerOf: (UIViewController *)vc {
if ([vc isKindOfClass:[UINavigationController class]]) {
return [self _getTopViewControllerOf:[(UINavigationController *)vc visibleViewController]];
}
else if ([vc isKindOfClass:[UITabBarController class]]) {
return [self _getTopViewControllerOf:[(UITabBarController *)vc selectedViewController]];
}
return vc;
}
获取app key window
+ (UIWindow * _Nullable)getAppKeyWindow {
UIWindow *window = nil;
if (@available(iOS 13.0, *)) {
UIWindowScene *sc = (UIWindowScene *)UIApplication.sharedApplication.connectedScenes.anyObject;
if (@available(iOS 15.0, *)) {
window = sc.keyWindow;
}
else {
window = sc.windows.firstObject;
}
}
else {
window = [UIApplication.sharedApplication.delegate respondsToSelector:@selector(window)]
? UIApplication.sharedApplication.delegate.window
: UIApplication.sharedApplication.keyWindow;
}
return window;
}
自定义格式化方法
- (NSString *)stringWithFormat:(NSString *)format, ...{
if(![format isKindOfClass:NSString.class]) {
format = [NSString stringWithFormat:@"%@",format];
}
va_list args;
va_start(args, format);
NSString *result = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
return result;
}
UINavigationController
当设置nav controller的view.tintColor = .red
时,会将title、barButtonItem的tintColor
都设置为该色。
Swift高斯模糊
radius
有效范围为0 ~ min(image.width, image.height)/image.scale
import CoreImage.CIFilterBuiltins
extension UIImage {
func blurredImage(radius: Float) -> UIImage? {
guard let cgImg = cgImage else {
return nil
}
let ci = CIImage(cgImage: cgImg)
let gaussFilter = CIFilter.gaussianBlur()
gaussFilter.inputImage = ci
gaussFilter.radius = radius
guard let ciImg = gaussFilter.outputImage?.cropped(to: ci.extent) else {
return nil
}
print(radius)
return .init(ciImage: ciImg)
}
}
更优雅的handler
和return
enum RequestError: Error {
case noData
}
func mockRequest(url: String, completion: @escaping (Result)->Void) {
guard let aURL = URL(string: url) else {
// 这里推荐这样的写法,也可以将`completion`和`return`分开写
return completion(.failure(URLError(.badURL)))
}
let task = URLSession.shared.dataTask(with: aURL) { data, response, error in
if let error = error {
return completion(.failure(error))
}
guard let data = data else {
return completion(.failure(RequestError.noData))
}
completion(.success(data))
}
task.resume()
}
mockRequest(url: getOneImageUrl) { result in
switch result {
case .failure(let error): print("Mock request failed: ", error)
case .success(let data):
print("Mock request success: ", data.count)
}
}
Swift 5.5 try await/async
1,使用async await,进行异步串行执行。
2,使用async let,在同一个Task(任务)内,进行异步并行执行。
3,使用group task和 for await,让多个Task并行执行。
Swift Pointer
var x = 42
var y = 3.14
var z = "foo"
var obj = NSObject()
func printPointer(ptr: UnsafePointer) {
print(ptr)
}
printPointer(ptr: &x)
printPointer(ptr: &y)
printPointer(ptr: &z)
printPointer(ptr: &obj)
// 0x000000011a145660
// 0x000000011a145668
// 0x000000011a145670
// 0x000000011a145688
Swift semaphore lock
fileprivate var lock: DispatchSemaphore = DispatchSemaphore(value: 1)
public func cancel() {
_ = lock.wait(timeout: DispatchTime.distantFuture)
defer { lock.signal() }
guard !isCancelled else { return }
isCancelled = true
cancelAction()
}
Swift 限制set/get
// 外部`get`,内部`set`
public fileprivate(set) var isCancelled = false
Swift case let as
switch progressAlamoRequest {
case let downloadRequest as DownloadRequest:
case let uploadRequest as UploadRequest:
case let dataRequest as DataRequest:
default: break
}
NSTextAttachment 对齐
let textAttachment = NSTextAttachment()
textAttachment.bounds = .init(x: 0, y: round(label.font.capHeight - 30)/2.0, width: 30, height: 30)
Swift enum if
let a = Animal.people("张三")
// 注意是=而不是==,类似 if let _ = a,是赋值操作。
if case .people(let name) = a {
print(name)
}
// 省略参数
if case .people = a {
}
// 只要枚举里带参数了,不带参数类型也需要使用case这样判断,也是=,而使用.other == a,就会报错。有意思。
if case .other = a {
print(name)
}
Swift 数字格式化
String(format: "%.2f", floatValue)
UIPanGestureRecognizer
let gestureIsDraggingFromLeftToRight = (recognizer.velocity(in: view).x > 0)
Swift 5 return
issue
AutoLayout
但在实际应用中,一般会用leadingAnchor和trailingAnchor来代替leftAnchor和rightAnchor。它们的区别在于,在一些喜欢从右往左阅读的地区,leadingAnchor实际代表右边,而trailingAnchor代表左边。在我国这种从左往右阅读的国家则可以认为leading==left,trailing==right。比起直接使用left和right,使用leading和trailing可以免去做适配的麻烦。这也是Auto Layout的优势之一。
button.translatesAutoresizingMaskIntoConstraints = NO;
translatesAutoresizingMaskIntoConstraints是为了兼容前朝遗老Auto Resizing所做出的遗憾逻辑。它会把Auto Resizing中描述的关系转化成Auto Layout中的约束。如果你要自己添加约束,就必须把这个字段设为NO。
Git set-url
git remote set-url origin [email protected]/user_name/repository_name.git
Swift Date funcs
extension Date {
fileprivate static let zone = Calendar.current.identifier
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var startOfMonth: Date {
let calendar = Calendar(identifier: Self.zone)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components) ?? Date()
}
var startOfWeek: Date {
let calendar = Calendar(identifier: Self.zone)
let components = calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
return calendar.date(from: components) ?? Date()
}
var startOfYear: Date {
let calendar = Calendar(identifier: Self.zone)
let components = calendar.dateComponents([.year], from: self)
return calendar.date(from: components) ?? Date()
}
var endOfDay: Date {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfDay) ?? Date()
}
var endOfMonth: Date {
var components = DateComponents()
components.month = 1
components.second = -1
return Calendar(identifier: Self.zone).date(byAdding: components, to: startOfMonth) ?? Date()
}
var endOfWeek: Date {
var components = DateComponents()
components.weekOfYear = 1
components.second = -1
return Calendar(identifier: Self.zone).date(byAdding: components, to: startOfWeek) ?? Date()
}
var endOfYear: Date {
var components = DateComponents()
components.year = 1
components.second = -1
return Calendar(identifier: Self.zone).date(byAdding: components, to: startOfYear) ?? Date()
}
}
}
enum ReportRange: String, CaseIterable {
case daily = "Today"
case monthly = "This Month"
case weekly = "This Week"
case yearly = "This Year"
func timeRange() -> (Date, Date) {
let now = Date()
switch self {
case .daily:
return (now.startOfDay, now.endOfDay)
case .weekly:
return (now.startOfWeek, now.endOfWeek)
case .monthly:
return (now.startOfMonth, now.endOfMonth)
case .yearly:
return (now.startOfYear, now.endOfYear)
}
}
CaseIterable allows you to iterate over the possible values of the enum you just defined. 可以访问
.allCases
刻度绘制
// Counter View markers
guard let context = UIGraphicsGetCurrentContext() else {
return
}
// 1 - Save original state
context.saveGState()
outlineColor.setFill()
let markerWidth: CGFloat = 5.0
let markerSize: CGFloat = 10.0
// 2 - The marker rectangle positioned at the top left
let markerPath = UIBezierPath(rect: CGRect(
x: -markerWidth / 2,
y: 0,
width: markerWidth,
height: markerSize))
// 3 - Move top left of context to the previous center position
context.translateBy(x: rect.width / 2, y: rect.height / 2)
for i in 1...Constants.numberOfGlasses {
// 4 - Save the centered context
context.saveGState()
// 5 - Calculate the rotation angle
let angle = arcLengthPerGlass * CGFloat(i) + startAngle - .pi / 2
// Rotate and translate
context.rotate(by: angle)
context.translateBy(x: 0, y: rect.height / 2 - markerSize)
// 6 - Fill the marker rectangle
markerPath.fill()
// 7 - Restore the centered context for the next rotate
context.restoreGState()
}
// 8 - Restore the original state in case of more painting
context.restoreGState()
In the code above, you:
- Save the original state of the matrix before you manipulate the context's matrix.
- Define the position and shape of the path, though you're not drawing it yet.
- Move the context so that rotation happens around the context's original center, indicated by the blue lines in the previous diagram.
Save the centered context state for each mark. - Determine the angle for each marker using the individual angle previously calculated. Then you rotate and translate the context.
- Draw the marker rectangle at the top left of the rotated and translated context.
- Restore the centered context's state.
- Restore the original state of the context before any rotations or translations.
Code Separate Guide
class MyViewController: UIViewController {
// class stuff here
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// table view data source methods
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// scroll view delegate methods
}
Xcode Copy Method Signature
put the cursor in the method name and press
Shift-Control-Option-Command-C
(all 4 modifier keys) and Xcode will kindly put the signature on your clipboard.
class FilmTableViewCell: UITableViewCell {
func configure(
title: String,
plot: String,
isExpanded: Bool,
poster: String
) {
...
}
}
//copied: FilmTableViewCell.configure(title:plot:isExpanded:poster:)
Xcode Playground
Creating a new playground. In Xcode, go to File ▸ New ▸ Playground
Swift @autoclosure
简化闭包/自动闭包
override func viewDidLoad() {
super.viewDidLoad()
test(a: 2>3, b: 3>4)
// also likes:
// test(a: 2>3, b: {3>4})
}
func test(a: Bool, b: @autoclosure ()->Bool) -> Bool {
if a {
return a
}
return b()
}
举例
func calculate(_ operating: @autoclosure ()->R) -> R {
operating()
}
calculate(2*3+4)
Swift init
OC的初始化方法,它必须返回一个instancetype类型的self实例,而这个self只能通过super的init方法初始化才行。
而Swift的初始化方法,既然被称为designated,就说明他是自己内部实现了自己的初始化逻辑,脱离了super的必须调用限制。
在Swift的初始化过程中,先保证自己内部的所有属性已经全部有了初始值(optional的除外),然后调用super的init方法告知自己的父类完成它的初始化过程,这样一级一级向上告知后,就能保证整个继承链下所有的类的属性是有初始值的,才能正确完成初始化过程,进一步优化内存的初始化。
由于convenience初始化方法可以被extension,所以它内部的内存分配处理过程必须由它内部已经存在的designated初始化方法来实现。
-
便捷初始化方法必须调用本类指定初始化方法
- A convenience initializer must ultimately call a designated initializer.
-
指定初始化方法必须调用父类指定初始化方法
- 重写父类 init() 必须先调用super.init(),然后赋值
- 实现空白 init() 则先赋值,后调用super.init()
-
要求初始化方法
- 子类必须实现
- You don’t write the override modifier when overriding a required designated initializer
.rpa文件解压
- 安装python
- 安装pip
curl https://bootstrap.pypa.io/get-pip.py | python3
- 安装unrpa
pip3 install unrpa
- 开始解压
python3 -m unrpa -mp "/path/folder" "/path/XXX.app/Contents/Resources/autorun/game/images.rpa"
Windows端口占用
- netstat -aon|findstr "8085"
- tasklist|findstr "14572"
- task >taskkill /T /F /PID
the port pid number
端口占用
- sudo lsof -i:(port)
- sudo kill (PID)
Swift tool-chain下载
https://www.swift.org/download
Xcode 版本下载
https://download.developer.apple.com/Developer_Tools/Xcode_12.5.1/Xcode_12.5.1.xip
xcodebuild 使用系统git终端代理解决github慢问题
xcodebuild -resolvePackageDependencies -scmProvider system
PublicAIPs
https://github.com/public-apis/public-apis
iOS15导航栏适配
if (@available(iOS 15.0, *)) {
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
appearance.titleTextAttributes = @{NSForegroundColorAttributeName :UIColor.greenColor};
appearance.backgroundColor = UIColor.redColor;
appearance.buttonAppearance.normal.titleTextAttributes = @{NSForegroundColorAttributeName: UIColor.greenColor};
appearance.doneButtonAppearance.normal.titleTextAttributes = @{NSForegroundColorAttributeName: UIColor.greenColor};
appearance.backButtonAppearance.normal.titleTextAttributes = @{NSForegroundColorAttributeName: UIColor.yellowColor};
self.navigationBar.scrollEdgeAppearance = appearance;
self.navigationBar.standardAppearance = appearance;
}
Xcode低版本调试真机高版本
- 复制
高版本Xcode对应的iPhoneOS15.0.sdk
到低版本Xcode的目录/Applications/Xcode12.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs
下 - 复制
15.0
对应的文件到低版本Xcode目录/Applications/Xcode12.5.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
下
Docker
https://kapeli.com/dash
免费icons
https://icons8.com
Font Bold Text
UIFontWeightUltraLight > UIFontWeightThin
UIFontWeightThin > UIFontWeightLight
UIFontWeightLight > UIFontWeightRegular
UIFontWeightRegular > UIFontWeightSemibold
UIFontWeightMedium > UIFontWeightBold
UIFontWeightSemibold > UIFontWeightHeavy
UIFontWeightBold > UIFontWeightBlack
UIFontWeightHeavy > UIFontWeightBlack
UIFontWeightBlack > UIFontWeightBlack
免费工具网站
https://juejin.cn/post/7010397195157372942
网易云音乐API
https://neteasecloudmusicapi.vercel.app
PicGo + Gitee 配置图床
PlanUML
https://plantuml.com/zh
免费图片api
https://picsum.photos
Swift 规范
https://juejin.cn/post/7017740209236213791
国内网速测试
http://www.17ce.com/site
UserDefaults 下标
// UserDefaults.standard[key] = value
extension UserDefaults {
subscript(key: String) -> Any? {
set{
setValue(newValue, forKey: key)
synchronize()
}
get{
return value(forKey: key)
}
}
}
汉字笔画库
HanZiWriter
https://hanziwriter.org/docs.html
Jasper 入门
https://blog.csdn.net/dullchap/article/details/51799070
深入iOS系统底层之crash解决方法
https://juejin.cn/post/6844903670404874254
iOS设备升/降级
https://www.163.com/dy/article/GDVG8MBH05373GLF.html
大陆身份证号计算规则
计算方法
1、将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2。
2、将这17位数字和系数相乘的结果相加。
3、用加出来和除以11,看余数是多少?
4、余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字。其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2。(即余数0对应1,余数1对应0,余数2对应X...)
5、通过上面得知如果余数是3,就会在身份证的第18位数字上出现的是9。如果对应的数字是2,身份证的最后一位号码就是罗马数字X。
例如:某男性的身份证号码为【53010219200508011X】, 我们看看这个身份证是不是符合计算规则的身份证。
首先我们得出前17位的乘积和【(5*7)+(3*9)+(0*10)+(1*5)+(0*8)+(2*4)+(1*2)+(9*1)+(2*6)+(0*3)+(0*7)+(5*9)+(0*10)+(8*5)+(0*8)+(1*4)+(1*2)】是189,然后用189除以11得出的结果是189÷11=17余下2,187÷11=17,还剩下2不能被除尽,也就是说其余数是2。最后通过对应规则就可以知道余数2对应的检验码是X。所以,可以判定这是一个正确的身份证号码。
Xcode已损坏,无法打开,您应该将其移至废纸篓
xattr -rc /Applications/Xcode.app
递归获取满足条件subview
extension UIView {
func subviews(where condition: (UIView) throws -> Bool) rethrows -> [UIView] {
var subs = try subviews.filter(condition)
for sub in subviews {
let matches = try sub.subviews(where: condition)
subs.append(contentsOf: matches)
}
return subs
}
}
16进制颜色转换
static func _hexColor(_ hexString: String) -> UIColor? {
var string = ""
if hexString.lowercased().hasPrefix("0x") {
string = hexString.replacingOccurrences(of: "0x", with: "")
} else if hexString.hasPrefix("#") {
string = hexString.replacingOccurrences(of: "#", with: "")
} else {
string = hexString
}
if string.count == 3 { // convert hex to 6 digit format if in short format
var str = ""
string.forEach { str.append(String(repeating: String($0), count: 2)) }
string = str
}
guard let hexValue = Int(string, radix: 16) else { return nil }
let red = (hexValue >> 16) & 0xff
let green = (hexValue >> 8) & 0xff
let blue = hexValue & 0xff
return .init(red: CGFloat(red)/255, green: CGFloat(green)/255, blue: CGFloat(blue)/255, alpha: 1)
}
百度地图SDK
https://lbsyun.baidu.com/index.php?title=iossdk/guide/
高德地图SDK
https://lbs.amap.com/api/ios-sdk/guide/
唤起:https://lbs.amap.com/api/amap-mobile/guide/ios/route
Moya URL编码问题
https://github.com/Moya/Moya/issues/1049
UIViewController的touchesBegan
事件处理问题
处理弹出对话框时,蒙层点击消失,内容区域点击不处理
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if !canMaskViewDismissable {
return
}
var isTouchInner = false
let list = event?.allTouches?.filter{ $0.view != nil } ?? []
for t in list
{
let loc = t.preciseLocation(in: contentView)
print(loc)
if contentView.bounds.contains(loc) {
isTouchInner = true
break
}
}
if canMaskViewDismissable, !isTouchInner {
close()
}
}
Fastlane打包上传蒲公英
安装:sudo install fastlane -NV
工程目录下:fastlane init
编辑:Fastfile
default_platform(:ios)
platform :ios do
puts "============ 打包开始 ==========="
lane :pkg do |options|
version = get_info_plist_value(path: "./YourApp/Supports/Info.plist", key: "CFBundleVersion")
config = options[:to]
channel = "development"
env = "Debug"
if config == "appstore"
channel = "app-store"
env = "Release"
end
gym(
configuration: "#{env}",
export_method: "#{channel}",
silent: true,
clean: true,
scheme: "RaveLand",
output_name: "YourApp_#{channel}_#{version}.ipa",
output_directory: "./build",
)
if config == "pgy"
puts "============ 开始上传蒲公英 ============ "
pgyer(api_key: "", user_key: "")
puts "============ 上传蒲公英成功 ============ "
end
puts "============ 打包完成 ============ "
system "open ./build"
end
end
蒲公英插件:fastlane add_plugin pgyer
打包:fastlane pkg
Github更新开源库
git add .
git commit -m 'MESSAGE'
git push
git tag x.x.x
git push --tags
pod trunk register EMAIL "USERNAME"
pod trunk push .podspec --allow-warnings --verbose