突发奇想,想写一个Mac的屏保。但是从网上找了好多,都是用OC写的,并且都是很久以前的。凤毛麟角里找到了一两篇swift的教程,折腾了两天总算完成了。写了一个仿黑客帝国里滚动文字的屏保,虽然丑了点儿,有些晃眼。但谁在乎呢,我又不用,实现才是目的。丑媳妇也要见公婆,先看一下最终效果😂
创建一个新项目
选择macOS选项,拉到最底下,最后一个,有一个Screen Saver。这位置,可见很少有人用啊!新建的时候你会发现,并没有勾选swift的选项,没关系,建完之后你就会发现:果然不是Swift的!先运行一下看看,在Products文件夹找到编译后的CoolScreenSaver.saver
,这是我自己项目起的名字,反正找到一个.saver
的文件就对了。然后右键Open with Extenal Editor
,就可以安装了。可以先预览一下一片漆黑的屏保,接下来就可以开始了。
新建一个swift的class,class选择ScreenSaverView
。这是Xcode会提示报错,我们需要要把ScreenSaver
引入进来,就可以解决了。怎么让程序支持swift呢?在Build Settings -> Building Options
里找到Always Embed Swift Standard Libraries
,全部设置为YES
。然后怎么让Xcode编译swift文件,而不是OC文件呢?好吧,在info.plist
中,将Principal Class
设置成你的Swift文件中的class,这样就可以使用swift编写了,两个OC文件可以删除了。需要注意的是,当你再编译运行屏保的时候,可能会提示当前版本的macOS不能运行你的屏保,联系管理员blahblah的东西,据说可能是那些swift库没有自动导入,可以通过万能的重启解决!还有一个问题需要注意,你编译并重新安装了新的屏保,系统不会实时刷新,需要关闭之前的偏好设置,重新打开才能预览重新编译之后的结果。嗯,我在这折腾了好久,以为自己写的没有效果,原来是这个原因,妹的!
以上其实就是最主要的了!最难的不是代码,而是这些设置😂
ScreenSaverView的几个方法
我们的这个屏保主要用的这几个方法
|
|
命名这么明显了,就不多说了。主要工作在animateOneFrame
,或者在draw
方法里完成。如果是在draw
里完成更新,那么需要在animateOneFrame
中设置needsDisplay=true
。主要的思路就是:
- 生成一行行竖排文字
- 随机分布在屏幕各个角落
- 每一帧重新绘制的时候改变文字的纵坐标
- 文字出屏之后,重新设置拉回到顶部
主要实现
首先我们先声明一个结构体,用于存储文字坐标等信息。
|
|
声明几个变量
|
|
在初始化方法里随机生成一堆字符串,并生成所需要的一堆TextFieldSet
。之后就可以用这一堆TextFieldSet
,在屏幕上进行渲染重绘等操作了。
|
|
里面生成随机字符串的方法:
|
|
里面获取字符串某个字符位置的方法,着实折腾了一番,基础不够。字符串没法直接使用Int
类型的下标来获取某一个字符,只能用String.Index
这样一个东西来获取。但我又没找到相关Sting.Index
的生成或转译方法。只能通过截取字符串的形式了😂
再来看看生成TextFieldSet
的方法,collectTextField:
|
|
上面这段主要把文字分割,然后用换行重新拼接起来,这样文字就竖排了。没有找到相关直接竖排的属性或方法,只有先这样了。然后生成NSTextField
,并将其自身,以及相关坐标等信息存储到textFields
中。接下来看看生成NSTextField
的方法:
|
|
这里面随机生成了NSTextField
横纵坐标,同时为了效果更好看一点,稍加美颜,加了随机透明度和随机速度,这样就之后可以看到文字深浅的不同,以及运行速度的不一了。这样,我们所有有用的东西都存在了textFields
这个变量里了,接下来就可以用这里面的内容,实现绘制跟更新了。
|
|
不断的改变每一个NSTextField
的纵坐标。需要注意的是,坐标轴的原点竟然是屏幕左下角,前端都是左上角!感觉有些别扭😂
接下来在draw
方法或animateOneFrame
方法中调用updateTextPosition
,就可以不断更新了!速度可以自己调。不知道是电脑卡,还是调的不好,还是需要优化,我觉得好卡😂
最后说一点,如果你想调试屏保,我只找到一个比较简单,但却没什么用处的方法。那就是给项目新建一个target,在这个target的Appdelegate
中实例化你的屏保,并将其添加到window.contentView
里。这样在屏保程序打个断点,编译新建的target
,就可以调试了。但是,我实验的是结果,屏保根本没有在target
中绘制,只是init了,所以我只能调试到init方法。
最后,附上GitHub地址:screensaver