收集一些平时在iOS开发中碰到的问题解决方法
WKWebView中的Javascript交互
在WKWebView中,Javascript与OC的交互变的十分简单。只需要通过WKScriptMessageHandler
代理中的userContentController:didReceiveScriptMessage:
即可在网页中让Javascript发送消息给OC。
首先试着写一段js脚本1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var c=document.createElement('input');
c.type='checkbox'; 2.name='accept'; c2.id='accept'; c.checked=true;
var label = document.createElement('label');
label.htmlFor = 'accept';
label.appendChild(document.createTextNode('accept'));
document.getElementsByTagName('body')[0].appendChild(c);
document.getElementsByTagName('body')[0].appendChild(label);
var script = document.createElement('script');
script.innerHTML = \"
document.getElementById('accept').onclick = function toggle() {
var obj = document.getElementById('accept');
window.webkit.messageHandlers.accept.postMessage({'accept':obj.checked});
};
\";
document.getElementsByTagName('body')[0].appendChild(script);
这段js很简单,就是添加了一个checkbox
元素,然后在点击事件中通过postMessage
方法就可以在OC获取到js中传过来的对象。
接下来则是配置WKUSErContentController
:1
2
3
4
5
6
7
8
9
10
11
12
13WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];
//在文档末尾追加javascript
WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[configuration.userContentController addUserScript:script];
//添加需要用作连接的对象
[configuration.userContentController addScriptMessageHandler:self name:@"accept"];
WKPreferences *preferences = [[WKPreferences alloc] init];
preferences.javaScriptEnabled = YES;
configuration.preferences = preferences;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
在这里通过addScriptMEssageHandler
方法定义了一个js通知oc的对象accept,就是js中的messageHandlers
后边声明的对象。
最后ViewController实现WKScriptMessageHandler
代理,通过下列方法即可获得传输过来的数据:1
2
3
4
5
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"message name: %@, body: %@", message.name, message.body);
//...
}
这里通过message.name
可以获得传输对象的名称,就是上边定义的accept
,而message.body
则是获取到从js中传过来的数据
iOS应用内切换语言
How to force NSLocalizedString to use a specific language
ios开发应用内实现多语言自由切换
使用系统内置的NSLocalizedString(key,comment)
函数即可获得对应的本地化语句。如果需要在应用内切换其他语言,可以自行添加一个定义1
2
[[NSBundle bundleWithPath: [[NSBundle mainBundle] pathForResource: [NSString stringWithFormat:@"%@",[[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] firstObject]] ofType:@"lproj"]] localizedStringForKey:(key) value:@"" table:nil]
然后切换语言需要修改AppleLanguages
中的数组数据位置1
2[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"en-US", @"zh-Hant", @"zh-Hans", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
将需要切换的语言放置到第一个元素中即可
页面组件自适应导航栏
Status bar and navigation bar appear over my view’s bounds in iOS 7
当使用Interface Builder来构建页面的时候,如果是UIScrollVIew
或者其子类UITableView
的时候,当页面显示了NavigationBar
的时候,UIScrollVIew
部件会自动将坐标下移到NavigationBar
下方,
这是因为automaticallyAdjustsScrollViewInsets
属性为YES
,如果不希望scroll view
自动适应,将其设置为NO
即可。
如果是普通的页面,一般都会直接拖到最顶层,如果这个时候页面显示了NavigationBar
时,会将页面布局的一部分遮挡掉,以往的做法是将布局下移64,但是iOS7之后可以利用edgesForExtendedLayout
来设置:1
2
3
4
5
6- (void)viewDidLoad {
if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {
self.edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeRight | UIRectEdgeBottom;
}
...
}
跳转页面之后背景半透明
当使用presentViewController
方法弹出下一个UIViewController
的时候,如果需要这个视图背景半透明,需要设置如下:1
2
3
4
5
6UIViewController *viewControllers = [UIViewController new];
self.definesPresentationContext = YES;
viewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
viewController.backgroudColor = [UIColor colorWithWhite: 0.1 alpha: 0.5];
//如果源视图不是NavigationController子视图,直接用self即可
[self.navigationController presentViewController:viewController animated:NO completion:nil];
隐藏状态栏
IOS7如何隐藏状态栏,貌似之前的没效果了
How do I hide the status bar in a Swift iOS app?
在需要隐藏状态栏的ViewController
中重写prefersStatusBarHidden
方法1
2
3- (BOOL)prefersStatusBarHidden {
return YES;
}
然后在需要更改隐藏状态的地方调用setNeedsStatusBarAppearanceUpdate
方法1
[self setNeedsStatusBarAppearanceUpdate]
swift
写法1
2
3override func prefersStatusBarHidden() -> Bool {
return true
}
在应用中打开另一个应用
利用openURL,在IOS应用中打开另外一个应用
canOpenUrl - This app is not allowed to query for scheme instragram iOS9
iOS: Access app-info.plist variables in code
首先需要在info.plist
注册自定义的URL scheme
1
2
3
4
5
6
7
8
9<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>test1</string>
</array>
</dict>
</array>
在iOS9
以后,还需要添加以下属性:1
2
3
4<key>LSApplicationQueriesSchemes</key>
<array>
<string>test2</string>
</array>
这里意为应用可以打开scheme
为test2
的应用。
然后在代码中可以使用openUrl
函数打开应用:1
2
3
4
5
6
7
8
9//获取scheme
let prefix = (Bundle.main.object(forInfoDictionaryKey: "LSApplicationQueriesSchemes") as! NSArray)[0]
let url = URL(string: "\(prefix):client_id=test1&scoe=wopi&platform=iOS&app=test1&action=12345")
if UIApplication.shared.canOpenURL(url! as URL) { //判断能否打开
//打开应用
UIApplication.shared.openURL(url! as URL)
} else {
print("can not go to this app!")
}
接下来应用可以在AppDelegate
类中的handleOpenUrl
方法中获取到传过来的参数:1
2
3
4
5
6
7
8
9
10
11func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
print(url)
let viewController = self.window?.rootViewController
let message = url.absoluteString
let alertController = UIAlertController.init(title: "url", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "OK", style: .default, handler: nil))
viewController?.present(alertController, animated: true, completion: nil)
return true;
}
在网页中打开应用
在网页<script>
块中直接定义跳转的scheme
为应用自定义的scheme
即可
1 | //如果无法打开应用则跳转到AppStore中 |
UIButton 内容左对齐
设置contentHorizontalAlignment
属性1
[button setContentHorizontalAlignment: UIControlContentHorizontalAlignmentLeft];
或者使用UIEdgeInsetsMake(top, left, bottom, right)
方法来设置缩进,正数为缩进,负数为突出1
[button setContentEdgeInsets: UIEdgeInsetsMake(0, -20, 0, 0)];
裁剪照片
How to crop an image from AVCapture to a rect seen on the display
通过AVCaptureSession
以及AVCaptureStillImageOuput
获取的照片默认填充整个屏幕,与自定义的显示屏幕并不相同,因此需要裁剪照片至所见区域
1 | - (UIImage *)cropImage: (UIImage *)image { |
从照片文件夹中获取图像资源写入到临时文件夹
ALAsset , send a photo to a web service including its exif data
1 | ALAsset *selectedAsset = [self.selectAssets objectForKey:key]; |
通过PHAsset获取资源文件大小
How can I determine file size on disk of a video PHAsset in iOS8
iOS8之后,ALAsset
被标记为不推荐,取而代之的是PHAsset
。如果要获取文件大小,不能使用asset.defaultRepresentation.size
了,需要用到以下方法: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
26PHAsset *asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[cloudObject.tempFilePath] options:nil] firstObject];
if (asset.mediaType == PHAssetMediaTypeImage) {
PHImageRequestOptions *imageOptions = [[PHImageRequestOptions alloc] init];
imageOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
imageOptions.synchronous = YES;
imageOptions.networkAccessAllowed = NO;
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:imageOptions resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
NSLog(@"length %f",imageData.length/(1024.0*1024.0));
}];
} else if (asset.mediaType == PHAssetMediaTypeVideo) {
PHVideoRequestOptions *videoOptions = [[PHVideoRequestOptions alloc] init];
videoOptions.version = PHVideoRequestOptionsVersionOriginal;
videoOptions.networkAccessAllowed = NO;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:videoOptions resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
NSNumber *size;
[urlAsset.URL getResourceValue:&size forKey:NSURLFileSizeKey error:nil];
NSLog(@"size is %f",[size floatValue]/(1024.0*1024.0));
NSData *data = [NSData dataWithContentsOfURL:urlAsset.URL];
NSLog(@"length %f",[data length]/(1024.0*1024.0));
}
}];
}
不要忘了引入框架@import Photos
CMTimeMake
CMTimeMake(a,b)
: a当前第几帧, b每秒钟多少帧.当前播放时间a/bCMTimeMakeWithSeconds(a,b)
: a当前时间,b每秒钟多少帧
更新播放时间
使用addPeriodicTimeObserverForInterval:queue:usingBlock
方法可以监听到播放时间的变化
1 | __weak typeof(self) weakSelf = self; |
使用完需要移除观察者[self.player removeTimeObserver: playerObserver]