iOS如何在SDK中使用資原始檔(xib,image,localizable string...)

小東邪發表於2019-03-01

需求:在SDK中使用資原始檔,例如xib, image, 字串國際化等等.


閱讀前提:

  • 瞭解如何建立SDK
  • 瞭解如何在一個專案中匯入並使用SDK

GitHub地址(附程式碼) : 如何在SDK中使用資原始檔

簡書地址 : 如何在SDK中使用資原始檔

部落格地址 : 如何在SDK中使用資原始檔

掘金地址 : 如何在SDK中使用資原始檔


原理

SDK中使用xib,圖片或者其他資原始檔時不同於直接在一個專案中使用的方法,因為我們必須明確一點,SDK中只有.h,.m,.mm,.swift等檔案可以直接匯入使用,像圖片,xib檔案,做國際化的.string檔案等資原始檔無法直接通過載入Framework的路徑找到,我們必須要在專案Build Phases的Copy Bundle Resources新增我們需要使用的資原始檔(nib, image, .string...),因此,我們最好在SDK中新建一個Bundle檔案存放所有的資原始檔,然後在主工程中Copy Bundle Resources新增該bundle檔案即可載入所有SDK中的資原始檔.

建立Bundle

為了統一存放SDK中所有資原始檔

注意:我們需要在每次編譯完成後手動或寫指令碼自動將資原始檔拷貝到Bundle中以至於才能在主工程中生效。

此後我們在SDK專案所用到的所有資原始檔都應該放到Bundle之中。

0-createBundle

情景分類

一.使用Xib檔案

1. Build SDK工程

如果專案中包含xib檔案,則專案每次編譯後會在Framework中將自動生成nib檔案,如下圖所示

1-nib檔案

2.將新編譯好的Framework放入工程中

  • 首先拷貝編譯好的framework到我們工程目錄中(即工程中進入Framework,不會自行百度)
  • 使用Nib檔案分為兩種方式
    • 直接將Nib檔案匯入主專案資原始檔中
    • (推薦)將Framework中編譯好的Nib檔案放入Bundle中,再將Bundle匯入專案的資原始檔中,好處是可以統一管理一個Framework中所有的資原始檔

2-nib檔案匯入專案

如果只使用nib則如下圖,否則可按相同方式將bundle匯入專案

3-niblocation

3. 使用SDK中的xib檔案

  • 直接使用framework中的nib檔案
    NSString *path = [[NSBundle mainBundle] pathForResource:@"XDXTestFramework" ofType:@"framework"];
    TestJumpVC *vc = [[TestJumpVC alloc] initWithNibName:@"TestJumpVC" bundle:[NSBundle bundleWithPath:path]];
    
    [self presentViewController:vc animated:YES completion:nil];
複製程式碼
  • (推薦)使用framework中bundle中的nib檔案。

因為我們在第二步中已經將bundle放入專案的copy bundle resources中,所以下面程式碼中使用[NSBundle mainBundle]

    NSString *path = [[NSBundle mainBundle] pathForResource:@"XDXAllResources" ofType:@"bundle"];
    TestJumpVC *vc = [[TestJumpVC alloc] initWithNibName:@"TestJumpVC" bundle:[NSBundle bundleWithPath:[path stringByAppendingString:@"/xibs"]]];

    [self presentViewController:vc animated:YES completion:nil];
複製程式碼

如果未進行第2步中將nib檔案匯入專案則程式會crash,並提示“Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </var/containers/Bundle/Application/D46C1A83-E6F4-4DB2-8F02-EC0ED05C6C99/XDXSDKResourceFileDemo.app> (loaded)' with name 'TestJumpVC''”

二.使用圖片

1. 將圖片拷貝到Framework中建立好的bundle中

4-SDK_Picture

2. 使用圖片,載入bundle中圖片的路徑(Note:必須與真實路徑相同,可參考Demo)

    // Test image
    NSString *path = [[[NSBundle mainBundle] pathForResource:@"XDXAllResources" ofType:@"bundle"] stringByAppendingString:@"/images/1.png"];
    self.testImage.image = [UIImage imageWithContentsOfFile:path];
複製程式碼

三.使用國際化語言適配(中英文適配)

1.建立.string型別檔案以支援中英文

5-create_strings

2. 設定strings檔案的Localize使其支援多種語言

6-set_strings

3. 設定專案支援多種語音

7-set_project
8-set_project_file

4. 在多語言檔案中新增對應的字元

9-set_string

5. 建立NSBundle分類提供類方法以支援在SDK中使用多語言

原理同上,即在budle中找到對應的中英文sting檔案,然後利用- (NSString *)localizedStringForKey:(NSString *)key value:(nullable NSString *)value table:(nullable NSString *)tableName NS_FORMAT_ARGUMENT(1);方法來檢索bundle中對應語言的字串。

程式碼中涉及APP手動選擇中英文模式,而我們這裡主要實現根據系統當前設定的語言環境,所以忽略人為設定語言的情況

// Note : Be consistent with you create bundle‘s name.
#define XDXRouterResBundle @"XDXAllResources"

@implementation NSBundle (XDXLocalizable)

+ (instancetype)XDX_localizableBundleWithBundleName:(NSString *)bundleName {
    static NSBundle *localizableBundle = nil;
    if (localizableBundle == nil) {
        if (!bundleName) {
            bundleName = XDXRouterResBundle;
        }
        NSString *bundleType = nil;
        if (bundleName && ![bundleName hasSuffix:@"bundle"]) {
            bundleType = @"bundle";
        }
        NSString *bundlePath = [[NSBundle mainBundle] pathForResource:bundleName ofType:bundleType];
        
        localizableBundle = [NSBundle bundleWithPath:bundlePath];
    }
    return localizableBundle;
}

+ (NSString *)XDX_localizedStringForKey:(NSString *)key {
    return [self XDX_localizedStringForKey:key value:nil];
}

+ (NSString *)XDX_localizedStringForKey:(NSString *)key value:(NSString *)value{
    NSBundle *bundle = nil;
    //NSString *language = [self getLanguageFromSystem];
    //NSString *language = [self getLanguageFromPlist];
    NSString * language = [self getLanguageFromDevelopersSetup];
    if (!language) {
        language = [self getLanguageFromSystem];
        NSLog(@"Current language is %@",language);
    }
    //從FrameworkTestBundle.bundle中查詢資源
    NSString *bundlePath = [[NSBundle XDX_localizableBundleWithBundleName:nil] pathForResource:language ofType:@"lproj"];
    bundle = [NSBundle bundleWithPath:bundlePath];
    value = [bundle localizedStringForKey:key value:value table:nil];
    return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
}

//這個設定語言是通過讀取當前系統使用語言
+ (NSString *)getLanguageFromSystem{
    NSString *language = [NSLocale preferredLanguages].firstObject;
    if ([language hasPrefix:@"en"]) {
        language = @"en";
    } else if ([language hasPrefix:@"zh"]) {
        if ([language rangeOfString:@"Hans"].location != NSNotFound) {
            language = @"zh-Hans"; // 簡體中文
        } else {
            language = @"zh-Hant"; // 繁體中文
        }
    } else {
        language = @"en";
    }
    return language;
}

//這個是設定語言通過Plist檔案來讀取
+ (NSString *)getLanguageFromPlist{
    NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"SDKInternationalizationDemoPlist.plist" ofType:nil];
    if (!bundlePath) {
        return nil;
    }
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
    if (dict) {
        NSInteger languageNum = [[dict valueForKey:@"language"] integerValue];
        switch (languageNum) {
            case 1:
                return @"en";     //語言為英語:en
                break;
            case 2:
                return @"zh-Hans";//語言為簡中:zh-Hans
                break;
            case 3:
                return @"zh-Hant";//語言為繁中:zh-Hanz
                break;
            default:
                return @"en";
                break;
        }
    }
    return @"en";
}

//這個是設定語言通過開發者手動呼叫,從NSUserDefaults裡面去讀kDSADLanguageStyle這個欄位是哪一種語言
+ (NSString *)getLanguageFromDevelopersSetup{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSInteger languageStyle = [[userDefaults valueForKey:@"TODO"] integerValue];
    if (!languageStyle) {
        return nil;
    }
    switch (languageStyle) {
        case 1:
            return @"en";
            break;
        case 2:
            return @"zh-Hans";
            break;
        case 3:
            return @"zh-Hant";
            break;
        default:
            return @"en";
            break;
    }
}

@end
複製程式碼

6.在程式碼中使用多語言字串

self.testLabel.text = [NSBundle XDX_localizedStringForKey:@"Test"];
複製程式碼

附:在Framework每次編譯後新增指令碼來拷貝資原始檔到Bundle中

原因

每當資原始檔有所改動,例如圖片的增加減少,strings檔案中新增新欄位等等,我們都需要手動將新的檔案放入Bundle對應的資料夾下,如果我們想利用指令碼自動放所有資原始檔,可利用Xcode特性。

操作

  • 首先,在專案中新建一個指令碼

    10-createScript

  • 其次,在指令碼中將我們專案的所有資原始檔拷貝到bundle中對應的目錄下(注意:檔案路徑必須正確,如遇到Framework中的nib檔案,專案的位置在個人電腦中是不用的,需要查詢並修改)

    11-writeScript

  • 最後,我們在Build完專案後,所有資原始檔會更新到最新

    12-getFile

相關文章