iOS程序員眼中的客戶端免登陸(數(shù)據(jù)遷移已更新)

一侣姆、前言,為什么要做免登陸

  • 2017年1月9日沉噩,蓄勢已久的小程序正式上線捺宗,著實(shí),張小龍 用完即走 的理念發(fā)揮的淋漓盡致川蒙,無需下載蚜厉,掃碼可用,用完即走
  • 2017年互聯(lián)網(wǎng)人口紅利結(jié)束了畜眨,那么接下來除了內(nèi)容的精耕細(xì)作外昼牛,就是提高流量的轉(zhuǎn)化率,然而在流量轉(zhuǎn)化為真實(shí)用戶的道路上康聂,一個(gè)登陸注冊的入口擋住了運(yùn)營活動(dòng)多少真金白銀砸出來的流量匾嘱?
  • 在談免登陸之前呢,我想先大概說下客戶端登錄早抠,想必大家都耳熟能詳霎烙,一般情況下需要包含以下幾個(gè)方面【括號內(nèi)部分為可選項(xiàng)】:
  • SNS 第三方快捷登陸
  • 郵箱+(驗(yàn)證碼)+密碼 登錄注冊
  • 手機(jī)號+驗(yàn)證碼+(密碼)登錄注冊
  • (提示用戶上次在本機(jī)登錄方式 && 賬號)
  • 毫無疑問,相比于手機(jī)號、郵箱的登錄注冊悬垃,第三方登錄是最方便的游昼,在第三方app已經(jīng)登錄的前提下,需要以下兩步操作:
    1. 用戶第一次打開app需要小手點(diǎn)一下第三方圖標(biāo)
    2. 跳轉(zhuǎn)到對應(yīng)app后尝蠕,點(diǎn)一下 確認(rèn)授權(quán) 按鈕即可返回自己的app完成登錄

但是:嫱恪!看彼!

  • 在用戶還沒體驗(yàn)到你app任何亮點(diǎn)之前廊佩,憑什么讓用戶進(jìn)行如此繁雜的操作,不要讓用戶思考靖榕!不要讓用戶麻煩标锄!尤其是用戶對隱私日漸重視的今天!茁计!且不說某麥某東等用戶賬號密碼泄露料皇,就說前幾天某德利用手中大數(shù)據(jù)強(qiáng)行一把秀優(yōu)越。星压。践剂。

我就問你要是凱迪拉克車主你還會(huì)用高德么?!(默默掏出褲兜里的地鐵卡看了一眼娜膘。逊脯。)

  • 結(jié)論是用戶是越來越重視自己的隱私的,用戶在使用 app 的時(shí)候也不想進(jìn)行任何多余的思考
  • 因此在用戶下載 app 之后第一次打開竣贪,要狠下心去掉一切不必要的彈框(除國行iOS10必須彈出的蜂窩網(wǎng)絡(luò)權(quán)限之外军洼,其他接收通知、定位等權(quán)限最好放在需要的時(shí)候再彈出)
  • 除特殊軟件(如網(wǎng)絡(luò)電話)必須使用電話號碼注冊的贾富,其他類似電商歉眷、內(nèi)容瀏覽牺六、交友軟件颤枪、工具類等 app,都應(yīng)該進(jìn)行免登陸操作先讓用戶體驗(yàn) app 的基本功能淑际,在一些深度使用的高級功能上個(gè)添加門檻畏纲,提示用戶進(jìn)行登錄注冊操作

二、來幾個(gè)常用 app 的例子

1. 今日頭條:
  • 打開 app 后以游客身份進(jìn)入春缕,可以進(jìn)行常規(guī)的新聞瀏覽盗胀、查看評論、收藏锄贼、分享票灰、消息反饋等操作
  • 進(jìn)行爆料、評論吹零、查看閱讀歷史等操作的時(shí)候彈出登錄框
點(diǎn)擊更多登錄方式效果
  • 登錄成功后殖侵,之前收藏的數(shù)據(jù)已遷移到正式用戶名下
  • 如果實(shí)在發(fā)送評論的時(shí)候觸發(fā)的登錄操作,登錄成功后評論發(fā)出菲语,提示用戶評論發(fā)送成功
2. 每日開眼
  • 同樣的惹盼,進(jìn)入 app 后可正常瀏覽庸汗,視頻狀態(tài)下進(jìn)行點(diǎn)贊操作觸發(fā)登錄,你看這位女施主懸浮在泳池中手报,享受著柔和的陽光和微微清風(fēng)蚯舱,那曼妙的身材真是讓作為用戶的我忍不住登錄,再退出掩蛤,再登錄枉昏。。盏档。

但是P钻!蜈亩!


如果你覺得我是因?yàn)榕魅斯恼掌排e這個(gè)例子懦窘,呵呵,在下可不是那么膚淺的人稚配,開眼的內(nèi)容和設(shè)計(jì)以及 app 整體流暢度都很棒畅涂,但是免登陸這里有兩個(gè)小瑕疵,在游客+橫屏狀態(tài)下

  • 觀看視頻的時(shí)候道川,點(diǎn)擊收藏按鈕午衰,直接modal出豎屏的登錄框,這點(diǎn)對用戶不是很友好
  • 登錄成功后冒萄,沒有自動(dòng)延續(xù)用戶在登錄之前的操作(收藏)

關(guān)于這兩點(diǎn)的技術(shù)實(shí)現(xiàn)后面會(huì)講

三臊岸、整體流程

  1. 用戶首次進(jìn)入 app 之后,判斷之前是否在本機(jī)登錄過尊流,如果是用戶首次登錄帅戒,就調(diào)用 游客登錄API,當(dāng)然這個(gè)游客 guestId 是服務(wù)器根據(jù)設(shè)備號生成的崖技,一般情況下逻住,一個(gè)設(shè)備對應(yīng)一個(gè)游客 guestId,而且這個(gè)游客 guestId 當(dāng)然是不能展示給用戶的(也可以在該接口返回一個(gè)上次登錄信息迎献,提示用戶上次登錄方式)

iPhone設(shè)備各種信息獲取傳送門

  • 然后使用這個(gè)游客 guestId 進(jìn)行各項(xiàng)參數(shù)的初始化瞎访,比如數(shù)據(jù)庫存取地址、下載文件路徑吁恍、瀏覽記錄等各方面操作的統(tǒng)計(jì)扒秸,當(dāng)然該游客在進(jìn)行一般操作的時(shí)候播演,就是使用這個(gè)游客 guestId 與服務(wù)器進(jìn)行交互
  • 接著就要考慮彈出登錄框的具體時(shí)機(jī),當(dāng)然每個(gè) app 的產(chǎn)品特性不一樣伴奥,一般會(huì)在以下幾種情況下彈出登錄框:收藏宾巍、評論、購買會(huì)員渔伯、下單購買商品等深度操作顶霞。
  • 還有就是萬萬不能在以下幾種情況下彈出登錄框:分享、用戶反饋锣吼、添加到購物車等选浑,因?yàn)檫@些操作是用戶主動(dòng)幫助分享app,提出意見玄叠,這時(shí)候彈出登錄框古徒,簡直是搞事情!
  • 彈出登錄框(注意橫豎屏的適配)读恃,用戶選擇進(jìn)行登錄后隧膘,獲取到一個(gè)正式的用戶 userId,重新初始化各項(xiàng)參數(shù)寺惫,隱藏登錄頁疹吃,進(jìn)行數(shù)據(jù)庫遷移合并、下載內(nèi)容路徑遷移(大多下載需要用戶相應(yīng)的權(quán)限西雀,防止作弊)萨驶、歷史記錄遷移合并、購物車內(nèi)容遷移合并等
  • 最后繼續(xù)進(jìn)行用戶需要登錄之前的操作(通過block來實(shí)現(xiàn))
  • 若用戶進(jìn)行退出登錄操作艇肴,先調(diào)用退出登錄的api腔呜,然后再調(diào)用游客登錄的api

四、上代碼之前再悼,談?wù)劦卿涀缘囊恍┬〖?xì)節(jié)

  • 進(jìn)入到登錄注冊頁后核畴,鍵盤應(yīng)立刻彈出,需要郵箱的彈出字母鍵盤冲九,需要手機(jī)號的彈出數(shù)字鍵盤
  • 當(dāng) 兩個(gè)輸入框內(nèi)容沒有都達(dá)標(biāo)之前谤草,action按鈕應(yīng)該設(shè)置為disabled
  • 輸入內(nèi)容的時(shí)候考慮小屏幕適配,自動(dòng)滑動(dòng)到合適位置
  • 在文本輸入框有內(nèi)容之后娘侍,右側(cè)應(yīng)該設(shè)置?按鈕咖刃,供用戶一鍵刪除
  • 賬號有沒有長度限制泳炉,類似電話格式的判斷在前端做比較方便憾筏,比如在密碼框 becomeFirstResponder 的時(shí)候,就直接判斷賬號格式花鹅,如果錯(cuò)誤需提示用戶
  • 密碼輸入框需要設(shè)置明文暗文按鈕氧腰,以供用戶隨時(shí)校驗(yàn)
  • 點(diǎn)擊登錄按鈕后彈出菊花(當(dāng)然我指的是 UIActivityIndicatorView,不是那個(gè)肥皂那個(gè)菊花)或者動(dòng)畫,防止多次發(fā)送網(wǎng)絡(luò)請求
  • 對于登錄注冊信息出錯(cuò),這個(gè)最好是能做到及時(shí)反饋古拴,考慮下web端注冊賬戶的時(shí)候箩帚,昵稱是否已被占用能夠在用戶輸入就提示,如果每次興沖沖輸入一大堆消息后黄痪,滿懷期待的點(diǎn)擊注冊按鈕紧帕,結(jié)果提示“您的昵稱已被占用”,你對這個(gè)網(wǎng)站的好感是不是會(huì)降低那么一丟丟桅打?因此最好能夠在保證用戶行為流暢的基礎(chǔ)上提示用戶是嗜,比如
    • 昵稱限制10位,那么輸入第11位的時(shí)候就應(yīng)該是無效的
    • 最好統(tǒng)一登錄注冊界面:用戶輸入手機(jī)號挺尾、郵箱之后鹅搪,實(shí)時(shí)查詢數(shù)據(jù)庫是否已注冊,然后更新按鈕狀態(tài)
  • 也需要考慮網(wǎng)絡(luò)超時(shí)遭铺、請求出錯(cuò)丽柿、服務(wù)器宕機(jī)、短信未發(fā)送成功等異常信息
  • 對于一些金融類相關(guān)的app魂挂,為了防止服務(wù)器被攻擊(當(dāng)然也)甫题,是不是要考慮同一IP請求兩次后添加驗(yàn)證碼(倒計(jì)時(shí)一般是前端固定的代碼)
  • 如果登錄失敗,提示的信息一定要準(zhǔn)確涂召,比如是驗(yàn)證碼錯(cuò)誤幔睬,還是賬戶名密碼錯(cuò)誤雖然這個(gè)提示信息一般都是服務(wù)器同學(xué)來做

五、代碼設(shè)計(jì):啥都別說了芹扭,都在代碼里

1. 首先在全局的控制器管理類寫一個(gè)彈出 view 的方法
/** 大多情況下默認(rèn)的添加方式麻顶,直接添加到最頂層的控制器上
  * title:彈出登錄框的提示語,如登錄后方可進(jìn)行評論
  * block:用戶被登錄框所阻攔的操作(注意循環(huán)引用)
  */
- (void)transferControlToPortalViewWithTitle:(NSString *)title block:(void(^)())block;
2. 然后在收藏等深度操作需要提示游客登錄的點(diǎn)擊事件里面判斷
- (void)favoredBtnTapped:(UIButton *)sender {
    // 如果是游客賬戶舱卡,就提示用戶進(jìn)行登錄操作辅肾,否則就進(jìn)行正常的收藏按鈕點(diǎn)擊事件
    if ([self.systemAccountManager isGuest]) {
       [self.systemVCManager transferControlToPortalViewWithTitle:@"登錄后可進(jìn)行收藏操作" block:^{
           [weakSelf doFavoredAction];
       }];
    } else {
       [self doFavoredAction];
    }
}
3. 比如要實(shí)現(xiàn)上文中提到的 今日頭條 樣式的登錄框,不能用 present 也不能用 modal轮锥,因?yàn)槟菢拥脑捝弦患壍目刂破饕晥D就會(huì)被移到另外一個(gè) Window 上矫钓,不能實(shí)現(xiàn)其在原界面添加半透明遮罩的效果,因此采用下列方式
[fatherVC addChildViewController:portalVC];
[fatherVC.view addSubview:portalVC.view];

此處更正一下舍杜,感謝 CZAnchor 提出的方法新娜,這里是可以通過 present 方式實(shí)現(xiàn)的,代碼如下:

UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
UIViewController *baseVC = rootVC;

while (baseVC.presentedViewController) {
   baseVC = baseVC.presentedViewController;
}

if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
    portalVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    baseVC.definesPresentationContext = YES;
    [baseVC presentViewController:portalVC animated:NO completion:^{}];
} else {
    baseVC.modalPresentationStyle = UIModalPresentationCurrentContext;
    [baseVC presentViewController:portalVC animated:NO completion:^{}];
}
4. 在調(diào)用登錄接口的成功回調(diào)里面既绩,需要進(jìn)行兩個(gè)操作
4.1 首先進(jìn)行數(shù)據(jù)遷移:
  • 已下載內(nèi)容文件 的遷移概龄,由于某些下載內(nèi)容是需要相應(yīng)權(quán)限的,因此都是每個(gè)賬號對應(yīng)一個(gè)存儲(chǔ)路徑饲握,也是在一定程度上防止賬號過分共享造成的利益損失
/** 遷移已下載的文件 */
#warning 關(guān)于游客狀態(tài)下下載的內(nèi)容私杜,需要考慮兩部分:
1. 登錄的正式用戶之前未在本機(jī)上登錄過蚕键,創(chuàng)建用戶的下載路徑后直接將游客的下載內(nèi)容全部遷移過去(若只是登錄過沒有下載內(nèi)容,就直接全部遷移過去)衰粹;
2. 登錄的正式有用戶之前在本機(jī)登錄過并有下載內(nèi)容锣光,則需要將兩個(gè)路徑下的下載內(nèi)容合并
- (void)transferDownLoadedFile {
    // 獲取下載文件根路徑
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *libraryDir = [paths objectAtIndex:0];
    NSString *rootFilePath = [NSString stringWithFormat:@"%@/%@",libraryDir,@"## 這里是項(xiàng)目中下載文件的路徑 ##"];
    
    // 分別獲取游客和正式用戶的下載路徑(方便起見直接使用對應(yīng)ID作為路徑名稱)
    NSString *guestPath = [NSString stringWithFormat:@"%@/%@", rootFilePath, self.accountManager.guestId];
    NSString *userPath = [NSString stringWithFormat:@"%@/%@", rootFilePath, self.accountManager.userId];

    // 獲取文件管理器
    NSFileManager *manager = [NSFileManager defaultManager];

    // 獲取游客的下載文件數(shù)組 
    NSError *error = nil;
    NSArray *guestFilesArr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:guestPath error:&error];
    if (error) {
        NSLog(@"contentsOfDirectoryAtPath guestPath:%@", error);
    }
    
    // 遍歷游客的文件
    for (NSString *fileName in guestFilesArr) {
        //  拼接處 該文件在 游客狀態(tài) && 正式用戶狀態(tài) 的存儲(chǔ)路徑
        NSString *guestFileDir = [guestPath stringByAppendingPathComponent:fileName];
        NSString *userFileDir = [userPath stringByAppendingPathComponent:fileName];
        // 如果正式用戶 下載文件中不包含該文件,就創(chuàng)建一下
        if (![manager fileExistsAtPath:userFileDir]) {
            [manager createDirectoryAtPath:userFileDir withIntermediateDirectories:YES attributes:nil error:&error];
        }

        BOOL isDir;
        if ([manager fileExistsAtPath:guestFileDir isDirectory:&isDir] && isDir) {
            error = nil;
            NSArray *childFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:guestFileDir error:&error];
            if (error) {
                NSLog(@"contentsOfDirectoryAtPath dir:%@", error);
            }
            // 遍歷該文件夾內(nèi)子文件铝耻,全部遷移到 正式用戶 名下的文件
            for (NSString *childFile in childFiles) {
                NSString *filePath = [guestFileDir stringByAppendingPathComponent:childFile];
                NSString *destPath = [userFileDir stringByAppendingPathComponent:childFile];
                error = nil;
                [manager moveItemAtPath:filePath toPath:userFileDir error:&error];
                if (error) {
                    DDLogError(@"moveItemAtPath to path error:%@", error);
                    //如果正式用戶下該文件存在(即用戶之前在本機(jī)登錄并下載過該文件)會(huì)報(bào)錯(cuò)誊爹,那么就將游客路徑下的改文件刪除
                    [manager removeItemAtPath:filePath error:&error];
                }
            }
        }
    }
}
  • 遷移數(shù)據(jù)庫:這部分內(nèi)容著實(shí)跟項(xiàng)目本分的業(yè)務(wù)、封裝關(guān)系太大瓢捉,在這里以一個(gè) video 文件的下載記錄為例替废,以 FMDB 為載體大概講一下思路
// 1. 獲取游客的 db 文件路徑 guestDataBasePath
// 2. 打開游客該 db 文件
fmDataQueue = [FMDatabaseQueue databaseQueueWithPath:path];
 [fmDataQueue inDatabase:^(FMDatabase *fmDatabase) {
     if ([fmDatabase open]) {
        [fmDatabase setShouldCacheStatements:YES];
        //  創(chuàng)建 SQL 語句
        NSString *sqlStr = [NSString stringWithFormat:@"%@%@%@%@%@%@%@",
                    @"CREATE TABLE IF NOT EXISTS MYVIDEO  (VIDEOID TEXT  PRIMARY KEY ",
                    @",videoname      TEXT",
                    @",info           TEXT",
                    @",coverfilename  TEXT",
                    @",urlpath        TEXT")"];
         BOOL isExecute = [fmDatabase executeUpdate:createStatement];
         if (isExecute) {
             // 如有必要,可檢查一下表結(jié)構(gòu)是否已升級泊柬,此處不再贅述
         } else {
             NSLog(@"error occured while creating MYVIDEO table");
         }
     } else {
     NSLog(@"open datebase failed");
     }
}

// 3. 查詢游客賬戶下已下載的 video
//  創(chuàng)建空數(shù)組用于存放 video 對象
NSMutableArray *videoArray = [[NSMutableArray alloc] init];
[fmDataQueue inDatabase:^(FMDatabase *fmDatabase) {
    // 書寫 sql 語句
    NSString *query = [NSString stringWithFormat:@"SELECT videoid,videoname,info,coverfilename,urlpath, FROM MYVIDEO "];
    NSString *sqlQuery;
    if (wheresql != nil) {
        sqlQuery = [NSString stringWithFormat:@"%@%@", query, wheresql];
    } else {
        sqlQuery = query;
    }
    // 按時(shí)間降序排序
    sqlQuery = [sqlQuery stringByAppendingString:@" ORDER BY time DESC "];
    FMResultSet *resultSet = [fmDatabase executeQuery:sqlQuery];
    if ([fmDatabase hadError]) {
        NSLog(@"FMDB Error %d: %@", [fmDatabase lastErrorCode], [fmDatabase lastErrorMessage]);
    }
    // 取出查詢的結(jié)果集
    while ([resultSet next]) {
        VideoClass *video = [[VideoClass alloc] init];
        video.videoId               = [resultSet stringForColumn:@"videoid"];
        video.videoTitle            = [resultSet stringForColumn:@"songname"];
        video.videoDescription      = [resultSet stringForColumn:@"info"];
        video.coverFileName          = [resultSet stringForColumn:@"coverfilename"];
        video.path                   = [resultSet stringForColumn:@"urlpath"];
       
        [videoArray addObject:video];
    }
    [resultSet close];
}];

// 4. 關(guān)閉游客 db
[fmDataQueue inDatabase:^(FMDatabase* fmDatabase) {
    if ([fmDatabase close]) {
        NSLog(@"close MYVIDEO succes ....");
    }
    else {
        NSLog(@"close MYVIDEO error");
    }
}];
[fmDataQueue close];
fmDataQueue = nil;

// 5. 打開 正式用戶 下的 db 文件(獲取游客 db 路徑后椎镣,代碼同上打開 游客 db)

// 6. 將 游客 下載的video 數(shù)據(jù)插入到 正式用戶的 db 中
[fmDataQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [array enumerateObjectsUsingBlock:^(VideoClass  *video, NSUInteger idx, BOOL * _Nonnull stop) {
        [self insertOrUpdateCourse:video withDB:db];
        // 創(chuàng)建插入數(shù)據(jù)的 sql 語句
        NSString *insertSql = @"INSERT OR REPLACE INTO MYVIDEO (videoid,videoname,info,coverfilename,urlpath,) VALUES(?,?,?,?,?)";
        BOOL result = [fmDatabase executeUpdate:insertSql,
                    video.videoId,
                    video.videoTitle,
                    video.videoDescription,
                    video.coverFileName,
                    video.urlPath];
        if (!result) {
            NSLog(@"操蛋!插入 MYVIDEO data failed");
        } else {
            NSLog(@"牛逼兽赁!Insert MYVIDEO data success, U did it!");
        }
    }];
}];

// 7. 合并數(shù)據(jù)庫成功后状答,根據(jù)游客 db 路徑,刪除 游客 db 文件
NSFileManager *fm = [NSFileManager defaultManager];
BOOL success = [fm removeItemAtPath:fullPath error:&error];
if (error) {
      NSLog(@"怎么會(huì)刪除失敗了刀崖,難道我姿勢不對惊科?delete file at path error:%@", error);
}

4.2 然后進(jìn)行隱藏登錄界面,并調(diào)用一下之前傳進(jìn)來的 block亮钦,繼續(xù)用戶之前的操作
- (void)hidePortalView {
    if (self.loginSucessBlock) {
        self.loginSucessBlock();
    }
    UIView animateWithDuration:0.2 animations:^{
        self.portalVC.view.alpha = 0;
    } completion:^(BOOL finished) {
        [self.portalVC.view removeFromSuperview];
        [self.portalVC removeFromParentViewController];
    }
}
5. 進(jìn)行橫豎屏適配
  • 由于帶有半透明背景的遮罩的視圖是以addChildViewController方式實(shí)現(xiàn)馆截,因此自動(dòng)適應(yīng)父控制器的橫豎屏,這里主要講一下再次點(diǎn)擊其他登錄方式 進(jìn)行賬號密碼輸入的傳統(tǒng)登錄注冊頁 的橫豎屏適配
- (void)signInWithAccountBtnTapped:(UIButton *)sender {
    SignInController *signInVC = [[SignInController alloc] initWithType:InputViewLogin];
    
    // 設(shè)置控制器的 modal 方式為遵循當(dāng)前控制器的環(huán)境蜂莉,實(shí)現(xiàn)當(dāng)前是橫(豎)屏就以橫(豎)屏方式modal
    signInVC.modalPresentationStyle = UIModalPresentationCurrentContext;
    
    [self presentViewController:signInVC animated:YES completion:nil];
}
  • 當(dāng)然蜡娶,在 SignInController 內(nèi)部也要進(jìn)行一些 UI 層級適配,在其 viewWillAppear 方法內(nèi)部實(shí)現(xiàn)以下方法
// 根據(jù)狀態(tài)欄方向得到當(dāng)前頁面橫豎屏信息
UIDeviceOrientation deviceOrientation = (UIDeviceOrientation)[UIApplication sharedApplication].statusBarOrientation;
// 根據(jù)橫豎屏狀態(tài)映穗,做出相應(yīng)的 UI 層級調(diào)整窖张,并做出相應(yīng)標(biāo)記
if (deviceOrientation == UIDeviceOrientationPortrait ||deviceOrientation ==
    UIDeviceOrientationPortraitUpsideDown) {
    [self doPortraitUIAdjustment];
    self.isLandScape = NO;
} else {
    [self doLandScapeUIAdjustment];
    self.isLandScape = YES;
}
  • 然鵝,跑一下代碼發(fā)現(xiàn)蚁滋,雖然橫豎屏的展示沒錯(cuò)了宿接,可是點(diǎn)擊輸入框后,鍵盤還是以豎屏的方式進(jìn)行展現(xiàn)辕录,因?yàn)槲覀冎皇前?SignInController 的 modal 方式和 UI 適配做了睦霎,此時(shí)控制器本身并不知道自己是橫屏還是豎屏,因此要重寫下面三個(gè)控制器方法
// 在橫屏狀態(tài)下走诞,應(yīng)該可以隨設(shè)備重力感應(yīng)進(jìn)行 LandscapeRight 和 LandscapeLeft 兩個(gè)方向的自動(dòng)翻轉(zhuǎn)
- (BOOL)shouldAutorotate {
    if (self.isLandScape) {
        return YES;
    } else {
        return NO;
    }
}

// 如果是橫屏狀態(tài)副女,應(yīng)該支持 LandscapeRight 和 LandscapeLeft 兩個(gè)方向,豎屏狀態(tài)下只支持 Portrait
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    if (self.isLandScape) {
        return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }
}

// 默認(rèn)的方向
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    if (self.isLandScape) {
        return UIInterfaceOrientationLandscapeRight;;
    } else {
        return UIInterfaceOrientationPortrait;
    }
}

#warning 至此速梗,橫豎屏適配算是大功告成了

大概的思路就是這些肮塞,由于跟項(xiàng)目相關(guān)性比較大,而且代碼實(shí)現(xiàn)方式也比較簡單姻锁,因此木有 demo枕赵,如果有其他問題歡迎在留言區(qū)進(jìn)行交流

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市位隶,隨后出現(xiàn)的幾起案子拷窜,更是在濱河造成了極大的恐慌,老刑警劉巖涧黄,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篮昧,死亡現(xiàn)場離奇詭異,居然都是意外死亡笋妥,警方通過查閱死者的電腦和手機(jī)懊昨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來春宣,“玉大人酵颁,你說我怎么就攤上這事≡碌郏” “怎么了躏惋?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嚷辅。 經(jīng)常有香客問我簿姨,道長,這世上最難降的妖魔是什么簸搞? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任扁位,我火速辦了婚禮,結(jié)果婚禮上趁俊,老公的妹妹穿的比我還像新娘贤牛。我一直安慰自己,他們只是感情好则酝,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布殉簸。 她就那樣靜靜地躺著,像睡著了一般沽讹。 火紅的嫁衣襯著肌膚如雪般卑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天爽雄,我揣著相機(jī)與錄音蝠检,去河邊找鬼。 笑死挚瘟,一個(gè)胖子當(dāng)著我的面吹牛叹谁,可吹牛的內(nèi)容都是我干的饲梭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼焰檩,長吁一口氣:“原來是場噩夢啊……” “哼憔涉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起析苫,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤兜叨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后衩侥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體国旷,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年茫死,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跪但。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡峦萎,死狀恐怖特漩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骨杂,我是刑警寧澤涂身,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站搓蚪,受9級特大地震影響蛤售,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妒潭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一悴能、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雳灾,春花似錦漠酿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匈庭,卻和暖如春夫凸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阱持。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工夭拌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓鸽扁,卻偏偏與公主長得像蒜绽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子桶现,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,510評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫躲雅、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評論 4 62
  • 早上好巩那!#幸福實(shí)修#~每天進(jìn)步1%#幸福實(shí)修10班陳月蓮/杭州 20170821~29/30 【幸福三朵玫瑰】 昨...
    馨兒_dda0閱讀 295評論 1 0
  • 文/姚小飛 早年間在鎮(zhèn)上的中心小學(xué)讀了三年書吏夯,村里也有小學(xué)校此蜈,身為一個(gè)文化工作者即横,父親對讀書還是比較重視,覺得鎮(zhèn)上...
    姚小飛閱讀 490評論 0 1
  • 圖片發(fā)自簡書App 加入潛能營可以說是偶然也可以說是有 意页藻。一直聽蟲蟲說IP營是個(gè)很虐人的地 方,就默默的記住了植兰,...
    中山正面管教閱讀 416評論 2 5