Xcode插件: MMNavigatorFont

前言


上周听了@makezl的插件开发直播介绍之后 萌生了写个插件的想法 目的就是为了解决一直以来让我很纠结的一个东西

Xcode的文件管理窗口的字体不等宽的问题
也就是这个东西

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

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

插件效果大概是这个样子

如何开发插件 这里就不介绍了 喵神的入门文章已经很好了
下面介绍一下开发过程中遇到的几个问题以及解决办法

閱讀全文

UIScrollview与Autolayout的那点事

前言


自从写了介绍Masonry那篇文章以后 就一直有人对UIScrollView的那个例子不是很理解

1
2
3
4
5
6
UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);
}];
  • 为什么要用一个container包含其他subview?
  • 为什么指定了edges 还要指定width? 不是多此一举吗?

那么今天我就按照我的理解来说明一下这个问题

閱讀全文

Tips:取消UICollectionView的隐式动画

UICollectionView在reloadItems的时候 默认会附加一个隐式的fade动画 有时候很讨厌 尤其是当你的cell是复合cell的情况下 (比如cell使用到了UIStackView)

下面几种方法都可以帮你去除这些动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

//方法一
[UIView performWithoutAnimation:^{

[collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]];
}];

//方法二
[UIView animateWithDuration:0 animations:^{
[collectionView performBatchUpdates:^{
[collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]];
} completion:nil];
}];

//方法三
[UIView setAnimationsEnabled:NO];
[self.trackPanel performBatchUpdates:^{
[collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]];
} completion:^(BOOL finished) {
[UIView setAnimationsEnabled:YES];
}];

如果你的APP只支持iOS7+ 推荐使用第一种方式performWithoutAnimation(感谢@sunnyxx的tip) 简单方便


but

问题还没有结束 上面介绍的方法只能解决UIView的Animation 如果你的cell中还包含有CALayer的动画 比如这样

1
2
3
4
5
6
- (void)layoutSubviews
{
[super layoutSubviews];

self.frameLayer.frame = self.frameView.bounds;
}

上述情况多用于自定义控件使用了layer.mask的情况 如果有这种情况 上面提到的方法是无法取消CALayer的动画的 但是解决办法也很简单

1
2
3
4
5
6
7
8
9
10
11
12
- (void)layoutSubviews
{
[super layoutSubviews];

[CATransaction begin];
[CATransaction setDisableActions:YES];

self.frameLayer.frame = self.frameView.bounds;

[CATransaction commit];

}

done!

Tips:关于MKMapView的一些操作小技巧

最近在优化地图的体验 用到了一些小技巧 这里分享一下

判断一个Annotation是否在当前地图中可见

1
2
3
4
5
6
7
MKMapRect visibleMapRect = self.mapView.visibleMapRect;
NSSet *visibleAnnotations = [self.mapView annotationsInMapRect:visibleMapRect];

if ( [visibleAnnotations containsObject:anno] )
{
//可见
}

将地图缩放到某个合适的位置 使一些Annotation同时可见

1
2
3
4
[self.mapView showAnnotations:@[anno1,anno2,anno3] animated:YES];

//使所有Annotation都可见
[self.mapView showAnnotations:self.mapView.annotations animated:YES];

控制MapView动画的时间

1
2
3
4
5
6
7
8

[MKMapView animateWithDuration:0.8 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{

[self.mapView setCenterCoordinate:anno.coordinate animated:YES];

} completion:^(BOOL finished) {

}];

如果将上面的功能组合起来 我们可以得到一个很不错的地图位置切换效果

Tips:获取APP的Launch Image

启动图(LaunchImage)的管理其实在iOS开始中算比较简单的了 尤其是Xcode引入了xcassets之后 完全是傻瓜式的操作 但是有的时候我们还是需要在Launch Image上做文章

LaunchImage在APP初始化完之后会立即消失并显示APP的界面 但是有的时候我们不希望它这么快就消失(比如有的人希望有个过渡效果 有的人希望等某些设置或者数据加载完之后再消失) 这也很简单 我们只要自己把LaunchImage再显示出来并且置顶就OK了

比如下面这样

不过我们配置了那么多适用于不同屏幕分辨率的LaunchImage 如何获取适合于当前屏幕分辨率的LaunchImage呢?

普通的办法是 把所有LaunchImage加入到工程并根据屏幕分辨率来命名 比如(640_960.png 640_1136.png …) 然后在程序中用代码拼接出对应的文件名 并引用

但是这种办法比较原始 而且万一以后苹果又出了一些其他分辨率的设备 或者启动图发生变化的时候 又需要人工的修改工程配置 不太好 而且还需要多占一份资源(APP的容量又变大啦)

而stackflow上的Cherpak Evgeny分享了一个更好的办法 直接读取NSBundle中的设置 即可获取当前适用的LaunchImage

我以上面的图为例写了个演示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
CGSize viewSize = self.window.bounds.size;
NSString *viewOrientation = @"Portrait"; //横屏请设置成 @"Landscape"
NSString *launchImage = nil;

NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
for (NSDictionary* dict in imagesDict)
{
CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);

if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
{
launchImage = dict[@"UILaunchImageName"];
}
}

UIImageView *launchView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:launchImage]];
launchView.frame = self.window.bounds;
launchView.contentMode = UIViewContentModeScaleAspectFill;
[self.window addSubview:launchView];

[UIView animateWithDuration:2.0f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{

launchView.alpha = 0.0f;
launchView.layer.transform = CATransform3DScale(CATransform3DIdentity, 1.2, 1.2, 1);

}
completion:^(BOOL finished) {

[launchView removeFromSuperview];

}];

这样就能轻松搞定LaunchImage了~