Xcode插件: MMNavigatorFont

来源:互联网 发布:微软sql server官网 编辑:程序博客网 时间:2024/05/29 13:50

Xcode的文件管理窗口的字体不等宽的问题

也就是这个东西

字体不等宽很难受有木有? 以前尝试过用TinkerTool 但是问题多多

趁着这周有时间 所以花了点时间做了个插件MMNavigatorFont来解决这个问题

插件效果大概是这个样子

1.gif

如何开发插件 这里就不介绍了 喵神的入门文章已经很好了

下面介绍一下开发过程中遇到的几个问题以及解决办法

问题

问题1 如何找到需要修改的对象

blob.png

如图 很明显我是要修改图中每一个控件的字体 但是我如何找到它呢?

首先想到的是监控所有的NSNotification 找出需要的通知

1
2
3
4
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(allNotitication:)
                                             name:nil
                                           object:nil];

但是找了半天 都没找到 这时候我想 要是有一款跟Reveal功能类似 可以查看OS X上的App结构的工具就好了 不过没找到(有知道的朋友可以推荐一下)

这时我想起了chisel 除了调试iOS的应用 也可以调试OS X的应用 试了一下 果然可以

blob.png

获取pviews命令打印出来的结构文本 搜索对应的关键字 就能找到我所需的view class了 这里我需要的就是这个DVTTableCellViewOneLine 再根据Xcode的头文件就可以知道 DVTTableCellViewOneLine是基于DVTTableCellView的 而DVTTableCellView中有如下两个成员

1
2
3
4
5
6
7
8
9
@interface DVTTableCellView : NSTableCellView
{
    ...
    ...
    DVTTableCellViewTextField *_titleTextField;
    DVTTableCellViewTextField *_subtitleTextField;
    ...
    ...
}

这就是我们需要修改字体的NSTextFiled

问题2 如何选择字体

因为不熟悉Cocoa 所以我也在网上一番搜索以及请教了@剑指人心以后 得到了如下的代码

1
2
3
4
5
6
7
8
9
10
- (void)actionChoose
{
    [[NSFontManager sharedFontManager] setDelegate:self];
    [[NSFontManager sharedFontManager] setTarget:self];
    [[NSFontManager sharedFontManager] orderFrontFontPanel:nil];
}
- (void)changeFont:(id)sender 
{
    self.selectedFont = [sender convertFont:self.selectedFont];
}

但是运行以后却有问题 字体选择框是弹出来了 但是始终获取不到选择的字体 而且changeFont中的sender(即[NSFontManager sharedFontManager])不为nil

经过一番尝试之后发现 必须为NSFontManager指定一个初始字体才可以

1
[[NSFontManager sharedFontManager] setSelectedFont:self.selectedFont?:[NSFont systemFontOfSize:13] isMultiple:NO];

不过这里我仍有一个疑问

在10.11中 NSFontManager的delegate已经被声明为deprecated了 但是我查了官方文档 也没有找到替代的东西 是否有同学知道如何在10.11中正确的使用NSFontManager呢 :)

blob.png

问题3 如何设置勾子

在cocoa中挂勾子肯定也是要用到Runtime的 这里我学习BBUFullIssueNavigator直接使用Aspects来hook

Aspects使用起来很简单 比如我在尝试了几次之后 发现在DVTTableCellViewOneLine的awakeFromNib方法执行之后对字体进行替换是最好的 那么只需要这样写即可

1
2
3
4
[objc_getClass("DVTTableCellViewOneLine") aspect_hookSelector:@selector(awakeFromNib)
                                                  withOptions:AspectPositionAfter
                                                   usingBlock:fontBlock
                                                        error:nil];

问题4 如何立即预览修改字体的效果

因为钩子是挂在awakeFromNib上的 所以当初始化完成之后 便无法再修改字体了 所以当字体发生变化的时候 需要遍历所有的DVTTableCellViewOneLine并修改其中的字体

这倒不是难事 关键在于如何保存包含这些DVTTableCellViewOneLine的容器 不然每次都要遍历整个Xcode的窗口 效率也未免太低了

经过对结构的观察 发现IDENavigatorOutlineView是比较合适保存的 但是IDENavigatorOutlineView会因为切换而重新生成 不能保存强引用 所以这里我定义了一个weak的NSView来保存它

1
@property (nonatomic, weak)   NSView *outlineView;

并且在viewDidMoveToSuperview中hook住

1
2
3
4
[objc_getClass("IDENavigatorOutlineView") aspect_hookSelector:@selector(viewDidMoveToSuperview)
                                                  withOptions:AspectPositionAfter
                                                   usingBlock:controlBarBlock
                                                        error:nil];

这样 在修改了字体之后 只要递归遍历其subviews并修改对应字体就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)refreshFont
{
    if ( self.outlineView )
    {
        [self refreshFontInView:self.outlineView];
    }
}
- (void)refreshFontInView:(NSView*)view
{
    for ( NSView *v in view.subviews )
    {
        [self refreshFontInView:v];
    }
     
    if ( [view isKindOfClass:NSClassFromString(@"DVTTableCellViewOneLine")] )
    {
        [self applyFont:view];
    }
}

问题5 如何还原默认字体

为了怕用户不喜欢修改过的字体 所以我设置了一个启用状态 当用户禁用的时候 会将所有字体还原成默认字体 这里就需要记录一下默认字体 很简单 我用Category为NSView添加了两个property

1
2
3
4
@interface NSView (MMNavigatorFont)
@property (nonatomic, strong) NSFont *originalTitleFont;
@property (nonatomic, strong) NSFont *originalSubtitleFont;
@end

然后在的hook函数中记录一下默认字体即可

1
2
3
4
5
6
7
8
9
NSView *view = info.instance;
if ( !view.originalTitleFont )
{
    NSTextField *titleTextFiled = [view valueForKey:@"_titleTextField"];
    NSTextField *subtitleTextFiled = [view valueForKey:@"_subtitleTextField"];
     
    view.originalTitleFont = titleTextFiled.font;
    view.originalSubtitleFont = subtitleTextFiled.font;
}

好了 至此一个功能完整的插件就完成了 如果你感兴趣 赶紧用一下吧 现在用alcatraz可以搜索得到了

blob.png

小结

一个功能简单的Xcode插件就这么诞生了 历时一天半的样子 虽然整个过程中也都是在摸石头过河 不过因为cocoa和cocoa touch开发起来确实有很多相似的地方 所以开发起来也不是非常的困难(也是因为功能简单的原因啦) 以后可能还会根据我自己的需求来开发更多的插件 XD

有同学说能不能改变Xcode的颜色 比如像我用的主题Monokai一样弄个暗色的主题 其实你完全可以自己开发一个插件来做这个事情 说不定还会火哦~

0 0
原创粉丝点击