iOS 小知识4
打包的坑
昨天在打包的时候,用 application loader
提交到 APP store
的时候,弹出来了一些错误提示,因此记录下:
1、一定要用最新的 XCode
去打包,有的时候 XCode
并不会提示自动更新,这点有些蛋疼,因为 APP store
不会经常打开,但是建议在每次要提交版本前一周的时候,看下是否 XCode
需要更新,不然可能会有错误和警告的提示;
2、使用 Carthage
打出来的 framework
,在内测环境下打的包是没有问题的,但是在上传到 APP store
的包进行出现了不支持 X86
架构的问题,着实意外,查了下在 build phases
添加一个 run script
并写入如下 shell
脚本并将 show enviroment
打钩即可:
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done
如果还是提示了错误,则进行如下操作:因为不是全部的 sdk
都需要,只修改对应的就可以了。
$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
"$APP_PATH" -name '[frameworkname].framework' -type d | while read -r
这里再次建议下同学们,Carthage
真的不是一个好用的东西,自己玩玩还可以,千万不要用在项目中来,如果真的要第三方库的管理工具,我建议还是 Cocoapods
,虽然坑也多,但是感觉还是比这个要好用多了,连喵神也在微博中吐槽过 Carthage
是他用过最糟糕的依赖库管理工具。
iOS 跳转到 App 设置界面
跳转到自己的项目中,在需要调转的按钮动作中添加如下的代码,就会跳转到设置中自己的app的设置界面,这里会有通知和位置权限的设置
NSURL * url = [NSURL URLWithString: UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL: url]) {
NSURL*url =[NSURL URLWithString: UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL: url];
}
测试注意:新项目测试,需要请求一下位置权限或者通知权限,才可以跳进自己的app设置里面,如果没有任何权限请求,就只能跳到系统的设置界面
跳转到系统设置的其他界面
在点击跳转的按钮动作中添加如下代码
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=WIFI"]]
跳转到其他的界面的字段:
About — prefs:root=General&path=About
Accessibility — prefs:root=General&path=ACCESSIBILITY
AirplaneModeOn— prefs:root=AIRPLANE_MODE
Auto-Lock — prefs:root=General&path=AUTOLOCK
Brightness — prefs:root=Brightness
Bluetooth — prefs:root=General&path=Bluetooth
Date& Time — prefs:root=General&path=DATE_AND_TIME
FaceTime — prefs:root=FACETIME
General— prefs:root=General
Keyboard — prefs:root=General&path=Keyboard
iCloud — prefs:root=CASTLE iCloud
Storage & Backup — prefs:root=CASTLE&path=STORAGE_AND_BACKUP
International — prefs:root=General&path=INTERNATIONAL
Location Services — prefs:root=LOCATION_SERVICES
Music — prefs:root=MUSIC
Music Equalizer — prefs:root=MUSIC&path=EQ
Music VolumeLimit— prefs:root=MUSIC&path=VolumeLimit
Network — prefs:root=General&path=Network
Nike + iPod — prefs:root=NIKE_PLUS_IPOD
Notes — prefs:root=NOTES
Notification — prefs:root=NOTIFICATIONS_ID
Phone — prefs:root=Phone
Photos — prefs:root=Photos
Profile — prefs:root=General&path=ManagedConfigurationList
Reset — prefs:root=General&path=Reset
Safari — prefs:root=Safari Siri — prefs:root=General&path=Assistant
Sounds — prefs:root=Sounds
SoftwareUpdate— prefs:root=General&path=SOFTWARE_UPDATE_LINK
Store — prefs:root=STORE
Twitter — prefs:root=TWITTER
Usage — prefs:root=General&path=USAGE
VPN — prefs:root=General&path=Network/VPN
Wallpaper — prefs:root=Wallpaper
Wi-Fi — prefs:root=WIFI
Setting—prefs:root=INTERNET_TETHERING
这些是私有 APIs,不建议使用,我曾经在使用这些 APIs 之后提交的 App 被拒,然后苹果给出的理由如下:
Your app uses the "prefs:root=" non-public URL scheme, which is a private entity. The use of non-public APIs is not permitted on the App Store because it can lead to a poor user experience should these APIs change.
Continuing to use or conceal non-public APIs in future submissions of this app may result in the termination of your Apple Developer account, as well as removal of all associated apps from the App Store.
虽然这个是在我提交无数个版本之后,突然被拒的,但是反馈的情况是非常严重的,非常建议大家如果在项目中使用了这些,还是去掉
求一张图片开销的正确姿势
在之前,我在看一张图片的开销的时候,都是直接将图片转为 NSData
,然后看 data
的大小来作为 image
的开销,其实这样是错误的,因为在将图片转为二进制的时候,图片都会压缩后存储,在看了 YY
的 YYWebImageView
之后,发现了正确的查看方式,如下:
- (NSUInteger)imageCost:(UIImage *)image {
CGImageRef cgImage = image.CGImage;
if (!cgImage) return 1;
CGFloat height = CGImageGetHeight(cgImage);
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
NSUInteger cost = bytesPerRow * height;
if (cost == 0) cost = 1;
return cost;
}
即先找到图片的 cgimage
,然后找到 cgimage
的高和每一行占用的比特数,然后乘以这两个数就行了;
app内切换语言
先搞懂系统给的国际化的API: 跟随系统切换, 多语言文件名必须是 Localizable
NSLocalizedString("label", nil);
下面三个都可以手动设置多语言
//第一个参数:是多语言中的key
//第二个参数:是多语言文件的名字
//第三个参数:是对key的注释说明,一般传nil
NSLocalizedStringFromTable(@"label", @"STLocalizable", nil);
//指定多语言文件名和bundle
NSLocalizedStringFromTableInBundle(@"label", @"STLocalizable", bundle, nil);
//在上面的基础上增加一个默认值的参数
NSLocalizedStringWithDefaultValue(@"label", @"STLocalizable", bundle, @"label", nil);
系统就是根据查找每个 bundle
里面 lproj
文件来找到对应的翻译,然后来根据翻译里面的 key
来设置对应的语言的; 如果跟随系统,则系统在设置之后,其实每个 APP
都被 kill
了一次,需要重新打开,然后才可以看到不同的语言;
那么如何在APP内修改语言呢,有一个博主在runtime修改了系统的默认方法 /* Method for retrieving localized strings. */
- (NSString *)localizedStringForKey:(NSString *)key value:(nullable NSString *)value table:(nullable NSString *)tableName
的实现,这个方法就是获取本地 strings
的方法;在修改的这个方法里强制使用设置的 language
,代码如下:
#import "NSBundle+Language.h"
#import <objc/runtime.h>
static const char _bundle = 0;
@interface BundleEx : NSBundle
@end
@implementation BundleEx
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
NSBundle *bundle = objc_getAssociatedObject(self, &_bundle);
return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end
@implementation NSBundle (Language)
+ (void)setLanguage:(NSString *)language {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
object_setClass([NSBundle mainBundle], [BundleEx class]);
});
objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
需要注意的是,当我们修改了之后,我们当前的 APP
之前界面的语言是已经获取了的,如果再重新来 refresh
会非常麻烦,微信的做法看了下,我猜测应该是重新创建 tabBarController
,然后 push
到设置界面,大家可以看下,当我们完成设置后都有一个到个人界面再 push
到设置的动画,目前我们也是采用这种方式做的;
这种方法有个问题就是,有些第三方,比如上下拉刷新的,自己就添加了多语言的,因此改变这些,需要我们手动修改第三方的库
网友评论