Latest

如何利用 UIScrollView 解决 UIView 长图片绘制失败的问题

最近在做一个分享需求的时候遇到了一个问题:将比较长的 UIView 绘制为 UIImage 的时候,会绘制失败,得到的是全黑/全白的图片 我们知道,将 UIView 绘制为 UIImage 的方法有以下几种: 1. 使用 UIView 的系统方法 - (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates; // example UIGraphicsBeginImageContextWithOptions(mainView.bounds.size, mainView.opaque, 0.0f); [mainView drawViewHierarchyInRect:mainView.bounds afterScreenUpdates:YES]; UIImage *snapShotImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 2. 使用 CAL

By Gray

Flutter 学习笔记

Dart 官方文档:https://dart.cn/guides/language/language-tour 变量 const 和 final 的区别 const值在编译时确定,final值在运行时确定。 方法 函数简写 如果函数体只有一个表达式,则函数可以简写为: bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; 参数 Dart 函数的参数有两种形式:必要参数和可选参数。必要参数位于所有参数的前面,可选参数则位于必要参数的后面。 如果一个函数声明里面有多个可选参数,那么调用方如何确定某个可选参数值是准确传给了目标参数呢? Dart 里面将可选参数分为了两种:可选命名参数和可选位置参数。 可选命名参数 使用{}将方法的某些参数划为可选命名参数。 可选命名参数通过在调用时明确给出目标参数的名字,来准确定位参数传递。 void getDetail(String name, {String branch = "

By Gray

shell 脚本零食铺

隐藏指令输出 如果想让终端不打印某条指令的运行结果,可以使用: command > /dev/null 2>&1 解决引入另一个脚本后,在其他路径执行脚本报错的问题 脚本 A 引入了脚本 B 和 C。如果不在脚本 A 所在的路径执行脚本 A,那么终端会因为找不到 B 和 C 而报错:CommonEcho.sh: No such file or directory 比较好的解决方案是在引入脚本 B 和 C的时候指明它们的路径(但不是写死路径): source $(dirname "$0")/lib_example source $(dirname "

By Gray

Swift 零碎知识

关于继承 SwiftUI 的解决方案 struct没有继承的功能,所以SwiftUI里面也不能像UIKit那样做写一个BaseView,然后子类继承里面的所有功能。网上有大神给出了这一点的解决方案 Creating BaseView class in SwiftUI 重写 1. 子类重写父类的属性后,也能通过super.someProperty获取父类的属性 关于类初始化 1. 通过给存储属性赋默认值,或者在初始化函数(包括便利构造器)中给存储属性赋值,不会触发该变量的didSet 2. 初始化构造器的三条法则: * A designated initializer must call a designated initializer from its immediate superclass. * A convenience initializer must call another initializer from the same class. * A

By Gray

Obj-C 零食铺

预处理 总览 宏指令 含义 #define 给一串代码指定一个常量,本质是「替换」 #include 一般用于导入头文件 #undef 将某个已经定义的宏移除 #ifdef 如果定义了某个宏,返回YES,等价于#if defined (...) #ifndef 如果没有定义某个宏,返回YES,等价于#if !defined (...) #if, #else, #elif, #endif 条件处理 #pragma 使用标准化方法向编译器发送某些命令,比如忽略某些警告 注意: define结尾不用带分号。因为define的本质是「替换」,如果带上分号的话,分号也会被替换到程序中。比如: #define MAXVAL 1000; // 报错,因为 MAXVAL 的值其实为「1;」,并不是整型 int y

By Gray

objc 关键字之 NS_UNAVAILABLE 和 NS_DESIGNATED_INITIALIZER

NS_UNAVAILABLE 简介 NS_UNAVAILABLE将方法标记为不可用,在类外调用该方法时会直接报错,提示'someMethod' is unavailable。但其实这个方法还能通过一些其他方式调用,比如performSelector 用处 * 禁用NSObject的init或者new方法,让开发者只能使用指定的初始化函数。 + (instancetype)new NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; - (instancetype)initWithParam:(NSString *)param; // 只能用这个方法初始化 * 整理代码的时候将某个方法标记为不可用,最后再把它删掉。 - (instancetype)init NS_UNAVAILABLE; - (instancetype)init __attribute_

By Gray

Swift Package 设置 file header

今天给自己的 Swift Package 仓库新增文件的时候,发现文件头的作者、时间、copy right 等信息不太全,一时兴起研究了一下。 一般来说,给 Xcode Project 新增文件时会带上 organization name。但是单独打开 Swift Package 时,是没有办法设置 organization name 的。这样新建的文件头部就会光秃秃的,不甚美观 一番研究之后发现,可以这样设置 Swift Package 的 file header: 1. 首先新建一个 IDETemplateMacros.plist 文件,并写入下面的内容: <?xml version="1.0" encoding="UTF-8&

By Gray

Dispatch Semaphore VS Dispatch Group

Dispatch Group Dispatch Group 在项目中比较常见,用于多任务多线程之间的协作。比如,使用两个队列分别请求不同的接口,等请求全部完成后,刷新页面。下面这段代码使用两个队列分别执行不同任务,当两个任务都完成后,通知主队列执行完成代码 import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let group = DispatchGroup() let q1 = DispatchQueue(label: "q1") group.notify(queue: .main) { print("Task All Done!") } q1.async(group: group) { for i in 1 ... 5

By Gray

Swift 单例中的线程安全问题

单例是常见的一种设计模式。最近在编写单例代码的时候,发现公司很多同事的 Swift 单例写法都是这样的, extension NSObject { @discardableResult static func kep_synchronized<T>(_ lock: AnyObject, closure: () -> T) -> T { objc_sync_enter(lock) defer { objc_sync_exit(lock) } return closure() } } class DefaultDict: NSObject { private static var manager: DefaultDict? static var sharedManager: DefaultDict { get { var newShared

By Gray

记一次错误使用 UIView Animation 引起的崩溃

上周做新版本的新手引导需求的时候,写了下面一段代码 @objc func nextStep() { // 点击事件 if currentStepIndex < viewChain.count { viewChain[currentStepIndex].clickBlock?() } // 隐藏当前步骤,展示下一个步骤 if currentStepIndex + 1 < viewChain.count { // 过渡动画,问题就出现在这里 UIView.animate(withDuration: 0.5) { self.viewChain[self.currentStepIndex].isHidden = true } completion: { finished in if finished { self.viewChain[self.currentStepIndex + 1].isHidden = false self.currentStepIndex += 1

By Gray