2025-05-08 00:00:00
用 Mac mini 外接第三方键盘时,音量调节可能会让人感到痛苦。比如,Fn + F11 和 Fn + F12 这对音量调节快捷键可能没法用,而基于它们的微调组合键(Fn + Option + Shift + F11 和 Fn + Option + Shift + F12)更是想都别想。
我的工作键盘是 IKBC C87,虽然有个 Fn 键,但它和 Mac 键盘的 Fn 完全不是一回事。键盘自带的音量调节组合键只能一格一格地调节,听歌时还好,但在 Coding 时,这“一格”的音量有时就显得过于喧闹,让人无法沉浸式思考。微调音量成了刚需。
最终,Karabiner-Elements 拯救了我。
只需将右 Ctrl 键映射为 Fn 键,就能像用苹果妙控键盘一样,正常使用各种基于 Fn 的快捷键,包括音量微调。
Karabiner-Elements 是一款强大的键盘映射工具,功能远不止映射单键这么简单。它还能:
此外,官网还提供了丰富的规则库,按需导入即可:https://ke-complex-modifications.pqrs.org/。
现在,我终于可以在 Coding 时享受“刚刚好”的背景音乐,而不用被“一格音量”的霸道支配。
希望能帮到和我一样有此困扰的你。
2025-04-23 00:00:00
在小数据量场景下,如何优化模糊搜索体验?本文分享一个简单实用的方案,虽然有点“土”,但效果还不错。
假设有一张表 t_course
,数据量在三到四位数,字段 name
需要支持模糊搜索。用普通的 LIKE
语句,比如:
SELECT id, name FROM t_course WHERE name LIKE '%2025数学高一下%';
结果却查不到 2025年高一数学下学期
。这就很尴尬了,用户体验直接拉胯。
首先想到 MySQL 的全文索引,但要支持中文分词得改 ngram_token_size
配置,还得重启数据库。为了不动生产环境配置,果断放弃。
接着想到 Elasticsearch,但对这么简单的场景来说,未免有点“杀鸡用牛刀”。于是继续寻找更轻量的方案。
最后想到一个“土办法”:先对用户输入进行分词,再用 MySQL 的 INSTR
函数匹配。简单粗暴,但很实用。
一开始用了 jcseg
分词库,写了个工具类:
public class JcSegUtils {
private static final SegmenterConfig CONFIG = new SegmenterConfig(true);
private static final ADictionary DIC = DictionaryFactory.createSingletonDictionary(CONFIG);
public static List<String> segment(String text) throws IOException {
ISegment seg = ISegment.NLP.factory.create(CONFIG, DIC);
seg.reset(new StringReader(text));
IWord word;
List<String> result = new ArrayList<>();
while ((word = seg.next()) != null) {
String wordText = word.getValue();
if (StringUtils.isNotBlank(wordText)) {
result.add(wordText);
}
}
return result;
}
}
本地测试一切正常,但部署到测试环境后,分词结果却变了!比如:
[2025, 数学, 高一, 下]
[2025, 数, 学, 高, 1, 下]
原因是 jcseg
在 jar 包中加载默认配置和词库时出问题了。网上的解决方案大多是外置词库,但我懒得折腾,决定自己撸个简易分词工具。
最终实现如下:
public class WordSegmentationUtils {
private static final List<String> DICT;
private static final String COURSE_SEARCH_KEYWORD_LIST = "数学,物理,化学,生物,地理,历史,政治,英语,语文,高中,高一,高二,高三";
static {
DICT = new ArrayList<>();
for (int i = 2018; i <= 2099; i++) {
DICT.add(String.valueOf(i));
}
DICT.addAll(Arrays.asList(COURSE_SEARCH_KEYWORD_LIST.split(",")));
}
public static List<String> segment(String text) {
if (StringUtils.isBlank(text)) {
return new ArrayList<>();
}
List<String> segments = new ArrayList<>();
segments.add(text);
for (String word : DICT) {
segments = segment(segments, word);
}
return segments;
}
private static List<String> segment(List<String> segments, String word) {
List<String> newSegments = new ArrayList<>();
for (String segment : segments) {
if (segment.contains(word)) {
newSegments.add(word);
String[] split = segment.split(word);
for (String s : split) {
if (StringUtils.isNotBlank(s)) {
newSegments.add(s.trim());
}
}
} else {
newSegments.add(segment);
}
}
return newSegments;
}
}
这个工具基于一个简单的词典 DICT
,按词典中的词对输入文本进行分割。比如:
2025数学高一下
[2025, 数学, 高一, 下]
现在,无论用户输入以下哪种形式,都能成功匹配到 2025年高一数学下学期
:
2025高一数学下
2025 高一 数学
数学高一2025
这个方案虽然简单,但在小数据量场景下,性能和体验都能满足需求,且实现成本低。如果遇到特殊情况,可以通过动态更新词典来解决。
当然,这种“土办法”并不适合复杂场景。如果需求升级,可以再考虑 MySQL 全文索引或 Elasticsearch。
最后,自己一个人负责开发和运维就是任性!如果有团队一起评审,这方案可能早就被否了吧……额,达摩克利斯之剑高悬。
2025-03-21 00:00:00
有没有这样一种奇妙体验:家里老人对你的忠告嗤之以鼻,却对网络医疗广告深信不疑?仿佛那些”三天速效”“纯天然”“祖传秘方”字样自带某种魔力,能让他们心甘情愿掏空钱包?
我爸就是这样。医院检查出肠道息肉后,他不愿接受正规治疗,却在网上找了个不知名的乡镇医院,买了一堆贵得离谱的中药。当我劝他去本市三甲医院时,我们吵了一架,不欢而散。
我百思不得其解:为什么长辈会信任网上随机广告,胜过亲生儿女的劝告?是年代差异让他们天然信任媒体?是搜索引擎大品牌的背书效应?还是他们骨子里相信”酒香也怕巷子深,神医总在小诊所”?
曾经,我尝试给他安装丁香医生,希望提供专业的医疗参考。结果他嫌上面评论太少,不够可信。转头却给我展示抖音上的”体外无痛胃肠检查”广告——零评论,明显广告标识。我内心:这双标技术堪称奥运冠军水平啊!
绝望之际,我发现了转机。
一天晚上,我爸向我展示他用”豆包”AI制作的视频。灵光乍现!我让他用豆包查询那个”神奇”的体外检查技术。豆包给出了客观分析,而他竟然接受了这个意见!
第二天他主动用豆包查询其他健康问题,我立刻顺水推舟:”以后不用上百度了,直接问豆包就好。”他居然欣然接受!
所以,我所谓的方法其实很简单:用 AI 助手替代浏览器和搜索引擎。无论是豆包、DeepSeek 还是其他 AI 产品,它们提供简洁明了的单一答案,不会展示五花八门的广告链接,也(暂时)不会被商业利益左右。
感谢技术进步,我终于不用在”爸,这是骗人的”和”儿子,你懂什么”之间无限循环了。
类似的救赎,tk 教主的经历显然更加硬核,但我可能来不及去建立那样的信任了:
2025-02-26 00:00:00
在包含视频播放功能的 App 中,一种常见的交互是在播放器界面的左侧上下滑动调节屏幕亮度,右侧上下滑动调节音量。我们的 iOS App 里也是这样设计的,但最近在测试过程中,发现亮度调节不生效了。
代码里面调节亮度的实现是这样的:
- (void)setBrightnessUp {
if ([UIScreen mainScreen].brightness >=1) {
return;
}
[UIScreen mainScreen].brightness += 0.01;
// ...
}
- (void)setBrightnessDown {
if ([UIScreen mainScreen].brightness <=0) {
return;
}
[UIScreen mainScreen].brightness -= 0.01;
// ...
}
这个实现在较早之前是没有问题的,那我首先想到比较可能是因为系统的更新,对这个 API 做了变更。于是先查阅了 UIKit/UIScreen/brightness 的官方文档,里面只提到了 brightness 属性只在 main screen 上被支持,取值范围是 [0.0, 1.0],以及亮度调节后,直到锁屏后才会失效——即使用户在锁屏之前已经关闭了 App。并没有看到什么值得特别留意的。
然后继续看代码里的 UIScreen.mainScreen,这个属性被标记为:
API_DEPRECATED("Use a UIScreen instance found through context instead: i.e, view.window.windowScene.screen", ios(2.0, API_TO_BE_DEPRECATED), visionos(1.0, API_TO_BE_DEPRECATED))
但当前在我使用的 SDK 18.2 版本中,这个属性应仍可正常使用。
在 Google 和 StackOverflow 找了一圈,大家讨论亮度调节不生效主要集中以下方面:
也没有找到什么能匹配我的场景的解决方案。
加了一些日志,在调节亮度前后分别打印了 brightness 的值,发现它在调用 setBrightness 方法后并没有发生变化,也没有报错和告警,看起来就像是这个方法根本没有被调用一样。
也做了一些其它尝试,比如把调整亮度的代码显式调度到主线程、使用 view.window.windowScene.screen
替代 UIScreen.mainScreen
等,但都没有效果。
无奈之下,我问了 GitHub Copilot 一嘴,它的回答是这样的:
我按它的建议检查了权限,确认了不存在权限问题。
有点绝望之际,看到它提供的代码里调整亮度的粒度是 0.1,而我的代码里是 0.01,于是我尝试将粒度改为 0.1,然后奇迹发生了,亮度调节生效了。
这就有点匪夷所思了……于是我又尝试了其它的粒度值,结果如下:
我找到安装了以前老版本 App 的一个老平板(iOS 10.3.3),在上面测试了一下,发现在这个版本上,0.01 的调节粒度是可以生效的。
也就是说,在 iOS (10.3.3, 18.2) 之间靠近后者的某个版本上,[UIScreen mainScreen].brightness 的调节粒度发生了变化,由 0.01 变为了 0.05。
至此破案了,顺便吐槽一下,官方文档里对此毫无提及,实在是……略坑。
2025-02-25 00:00:00
说来惭愧,独立支撑公司的软件系统已经一年有余,多数的精力都在开发和迭代 Web 服务与 Android 端,对于 iOS App 则是一直没有更新,遇到相关的 bug 反馈也是能拖就拖——毕竟,大多数情况下找个 workaround 还是不难的。
回过头想想,可能潜意识里一直有点犯怵,觉得 iOS 开发是自己的薄弱环节,所以总想着等有时间,再多学一点相关的东西,准备得更充分、更有自信能处理好了,再去更新。可一直这样下去也不是办法,所以春节前结合一些业务需求,我决定逼自己一把,尽快把 iOS App 更新一下。
面对一个个所谓难题:
然后在这个 AI 大行其道的时代,作为尊贵的 GitHub Copilot Pro 用户,在插件的辅助下光速添加了一个新的小功能,修复了一些 bug 后,我向 App Store Connect 提交了我的第一次版本审核,本以为需要经过漫长的等待,结果……
事情出乎意料地顺利,几个小时就通过了,这玩意也有新手保护期?
满怀得意我心欢喜,于是一鼓作气把囤积已久的几个 feature 给做了,然后兴冲冲地提交了第二次版本审核,结果……
几个小时后第一次被驳回,原因是:
Guideline 3.1.1 - Business - Payments - In-App Purchase
We found in our review that your app or its metadata provides access to mechanisms other than in-app purchase for purchases or subscriptions to be used in the app, which does not comply with the App Review Guidelines. Specifically:
- Your app's binary includes the following call-to-action and/or URL that directs users to external mechanisms for purchases or subscriptions to be used in the app:
User have to contact customer service to puchase credits.
看截图我查到了这是几年前参考一个大厂 App 实现的效果,在用户余额不足时,弹出一个提示框,上面有两个按钮,一个点击后展示了客服的联系方式,一个是「取消」,点击后跳转到充值页面。
我以为这里面的主要问题点是没有明确的「去充值」入口,导致审核人员以为用户无法直接充值,必须联系客服,于是我添加了一个「去充值」的按钮,将「取消」按钮的动作改为隐藏提示框,然后再次提交审核,结果几个小时后又被驳回了,原因仍然是:
- Your app's "xxx" page includes the following call-to-action and/or URL that directs users to external mechanisms for purchases or subscriptions to be used in the app
不过发过来的消息里还有这样一段:
Bug Fix Submissions
The issues we've identified below are eligible to be resolved on your next update. If this submission includes bug fixes and you'd like to have it approved at this time, reply to this message and let us know. You do not need to resubmit your app for us to proceed.
Alternatively, if you'd like to resolve these issues now, please review the details, make the appropriate changes, and resubmit.
其实,这时候我只要回复个消息,说我这次提交包含了一些 bug 修复,希望能通过审核,下次我再修复这个问题,就妥了……
但我当时脑子里不知道咋想的,可能是觉得这么个小问题,这次解决掉算了,然后就又改了一版,将那个跳转到客服联系方式的按钮去掉,又提交了一版,这时候我以为这把稳了,就收工等消息了。
回家正刷着沙雕视频呢,弹出来消息,又被拒了,这回是什么原因呢……
Guideline 2.1 - Performance - App Completeness
Issue Description
The app exhibited one or more bugs that would negatively impact App Store users.
Bug description: unable to load "xxx"
我人都懵了,同时懊恼万分,真不应该装这个逼,就该回个消息,让审核员先通过再说。
话说回来,这个 xxx 功能已经在线上跑了几年了,最近也没改过,最后思来想去,怀疑可能是审核员当时遇上了什么网络波动之类的,导致没加载出来。
没辙,我只好在一些设备上复测了一下,保存了一些该功能正常使用的截图,然后回复给审核员,表明这个功能经我多次测试是正常的,已经上线运行了几年且最近没有修改过,希望审核员能再次确认,如果可以的话帮忙通过审核。
然后这回真的是漫长的等待,等了两天多,度过了一个忐忑的周末后,终于在周一一大早盼来了好消息。这一个版本的审核历时五天,经过了三轮被拒,总算是磕磕绊绊地通过了:
以上就是我这个 iOS 开发新手的前两次 App 审核经历,总结一下,主要有以下几点:
总的来讲,相比 Android App 需要提交到各家应用市场,然后面临不同的审核标准和结果,iOS App 的审核体验相对还是不错的,毕竟只用面对唯一的渠道和标准。
2025-01-09 00:00:00
前一阵发生过两次 Mac mini 与蓝牙鼠标断连的情况,都是通过借用别人的有线鼠标来重新连接的,终究不方便。
后来就想着,能不能通过键盘来连接蓝牙鼠标呢?摸索了一番,找到了方法,在此记录一下。
先上操作演示:
看着上面的操作,是不是感觉 so easy?但实际上,我在操作过程中,一开始就遇到一个问题——焦点无法移动到设置面板右侧的按钮上。
这时我们要先了解一个关键的设置开关,以及它对应的快捷键:
该开关默认关闭,切换开关的默认快捷键是 Ctrl + F7。
通过键盘操作,打开系统设置:
按下 Cmd + 空格,调出 Spotlight 搜索框;
输入 系统设置,回车。
通过键盘上下键,定位到 蓝牙;
操作蓝牙鼠标,使其进入配对模式;(一般是长按鼠标底部的配对键)
连续按下 Tab 键,定位到蓝牙鼠标对应设备的 连接 按钮;
注意: 这里有可能发现,按 Tab 键焦点无法移动到设置面板右侧,这时就需用到我们前面提到的设置开关了,按下 Ctrl + F7,开启键盘导航功能,再按 Tab 键就可以移动焦点了。
按下 Space 键,连接蓝牙鼠标。
大功告成!
除上了述方法,我搜索的过程中还看到有一些其它思路,比如:
可以按需尝试和使用。