iOS 填坑系列 - 橫豎屏切換

概述

寫(xiě)代碼就是在不斷填坑的過(guò)程中慢慢成長(zhǎng),程序員哪有不遇坑的呢缔莲?

這篇文章來(lái)談?wù)刬OS中橫豎屏切換的一些坑掀宋,橫豎屏切換在App中很常見(jiàn)雳刺,本來(lái)我也以為做這個(gè)功能是很簡(jiǎn)單的一件事戚丸,但半年前我在做公司項(xiàng)目的過(guò)程中就遇到了不少麻煩划址,使用了一種比較tricky的方法,在屏幕方向切換時(shí)程序偶爾會(huì)崩掉限府,雖然后來(lái)經(jīng)過(guò)修改解決了夺颤,但導(dǎo)致控制屏幕方向的代碼散落在不同角落,不易閱讀胁勺,維護(hù)起來(lái)更不方便拂共。前陣子在寫(xiě)一個(gè)播放器JFPlayer時(shí),采用了另一種比較好的方法姻几,便在此總結(jié)一下各種坑吧。

這里分幾種情況:

  • 所有界面都支持橫豎屏切換
  • 只有一個(gè)(或幾個(gè))界面固定方向势告,其他界面支持橫豎屏切換
  • 只有一個(gè)(或幾個(gè))界面支持橫豎屏切換蛇捌,其他界面固定方向

一般情形

所有界面都支持橫豎屏切換

這是最簡(jiǎn)單的,只需要在【General】-->【Deployment Info】-->【Device Orientation】勾選上相應(yīng)地方向就行了咱台。


這樣設(shè)備是豎屏?xí)r所有界面都是豎屏的络拌,設(shè)備是橫屏?xí)r所以界面都是橫屏的。

注意:

這里有一個(gè)坑回溺,在 iOS 9 以后春贸,橫屏?xí)r狀態(tài)欄會(huì)隱藏混萝,如果想要顯示狀態(tài)欄,需要手動(dòng)控制萍恕。

Info.plist 中 設(shè)置 View controller-based status bar appearance 值為 YES逸嘀,在 view controller 中重寫(xiě) prefersStatusBarHidden 返回 false

override var prefersStatusBarHidden: Bool {
return false
}


## 特殊情形

### 只有一個(gè)(或幾個(gè))界面固定方向,其他界面支持橫豎屏切換

其實(shí)這種情況一般比較少見(jiàn)允粤,在【General】-->【Deployment Info】-->【Device Orientation】勾選希望支持的方向崭倘,然后在需要固定方向的視圖控制器中實(shí)現(xiàn)如下兩個(gè)方法即可。

```swift
override var shouldAutorotate: Bool {
    return true
}
    
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .landscape
}

只有一個(gè)(或幾個(gè))界面支持橫豎屏切換类垫,其他界面固定方向

這是最常見(jiàn)的情況司光,大多數(shù)App都會(huì)是這種需求,比如視頻直播類App悉患,只有一個(gè)界面需要支持橫豎屏残家,其他界面都是豎屏。

這種情況有兩種方法來(lái)實(shí)現(xiàn)售躁。

方法一

設(shè)置設(shè)備僅支持豎屏坞淮,監(jiān)聽(tīng)屏幕旋轉(zhuǎn)的通知,在收到通知后手動(dòng)旋轉(zhuǎn)視圖迂求。

不推薦D胙巍!

在【General】-->【Deployment Info】-->【Device Orientation】勾選方向:


取消屏幕自動(dòng)旋轉(zhuǎn):

override var shouldAutorotate: Bool {
    return false
}

viewDidLoad 中監(jiān)聽(tīng)通知:

NotificationCenter.default.addObserver(self, selector: #selector(didChangeOrientation), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

控制視圖旋轉(zhuǎn):

func didChangeOrientation() {
    if (UIDevice.current.orientation == .portrait) {
        UIView.animate(withDuration: 0.2, animations: {
            self.view.transform = .identity
            self.view.bounds = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        })
    } else {
        
        UIView.animate(withDuration: 0.2, animations: { 
            self.view.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI_2));
            self.view.bounds = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.height, height: UIScreen.main.bounds.width)
        })
    }
}
填坑一

這種方法需要精確地控制該界面的所有View(包括子View)的旋轉(zhuǎn)以及旋轉(zhuǎn)的方向揩局,設(shè)備豎屏?xí)r和橫屏?xí)r旋轉(zhuǎn)的角度不一樣毫玖,Home鍵在左和在右旋轉(zhuǎn)的角度也不一樣,在界面復(fù)雜時(shí)凌盯,特別麻煩付枫,所以不推薦。

填坑二

因?yàn)橹辉O(shè)置了豎屏驰怎,所以當(dāng)橫屏?xí)r阐滩,如果有鍵盤(pán)彈出,鍵盤(pán)是豎屏?xí)r的樣式县忌。

解決辦法:在【General】-->【Deployment Info】-->【Device Orientation】中加上橫屏?xí)r的方向掂榔。

注意:

在僅支持豎屏模式下,不能直接重載shouldAutorotate并返回true症杏。程序會(huì)崩掉装获,并拋出這個(gè)錯(cuò)誤

Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [Demo.ViewController shouldAutorotate] is returning YES'

原因是,你如果設(shè)備僅支持豎屏厉颤,不能在view controller中控制界面自動(dòng)旋轉(zhuǎn)穴豫。

方法二

設(shè)置設(shè)備支持橫豎屏,讓其自動(dòng)旋轉(zhuǎn)逼友,實(shí)現(xiàn)一個(gè)基類控制器只支持一個(gè)方向精肃,其他固定方向的界面繼承基類秤涩,同時(shí)監(jiān)聽(tīng)屏幕旋轉(zhuǎn)通知,處理一些特殊需求司抱。

強(qiáng)烈推薦?鹁臁!

在【General】-->【Deployment Info】-->【Device Orientation】勾選方向:


在基類控制器中重寫(xiě)兩個(gè)控制橫豎屏的方法:

class BaseViewController: UIViewController {
    override var shouldAutorotate: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }

}
填坑一

如果VieController 是放在UINavigationController或者UITabBarController中状植,需要重寫(xiě)它們的方向控制方法浊竟。

class RootNavigationController: UINavigationController {
    override var shouldAutorotate: Bool {
        guard let topViewController = topViewController else {
            return true
        }
        return topViewController.shouldAutorotate
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        guard let topViewController = topViewController else {
            return .all
        }
        return topViewController.supportedInterfaceOrientations
    }
    
    override var preferredStatusBarStyle: UIStatusBarStyle {
        guard let topViewController = topViewController else {
            return .default
        }
        return topViewController.preferredStatusBarStyle
    }
}
填坑二

播放器中都會(huì)有這樣一個(gè)功能,點(diǎn)擊按鈕將界面變成全屏津畸,該怎樣做呢振定?

答案是:將設(shè)備強(qiáng)制橫屏,改變狀態(tài)欄方向

func fullScreenButtonPressed(_ button: UIButton?) {
        
    // force change device and status bar orientation, that toggle the UIApplicationDidChangeStatusBarOrientation notification
    if isFullScreen {
        
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
        updateStatusBarAppearanceHidden(false)
        UIApplication.shared.statusBarOrientation = .portrait
    } else {
        
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
        updateStatusBarAppearanceHidden(false)
        UIApplication.shared.statusBarOrientation = .landscapeRight
    }
}

其實(shí)在 stackoverflow 上你能搜到另外一個(gè)答案

- (void)interfaceOrientation:(UIInterfaceOrientation)orientation
{
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector             = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val                  = orientation;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}

// 橫屏
- (IBAction)landscapAction:(id)sender {
    [self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
}

// 豎屏
- (IBAction)portraitAction:(id)sender {
    [self interfaceOrientation:UIInterfaceOrientationPortrait];
}

但我不知道如何將這段代碼改為Swift肉拓,也不知道這段代碼在 iOS 10 下是否仍然奏效后频。

填坑三

在大多數(shù)視頻類App中,播放視頻的窗口一開(kāi)始是在界面上方的一小塊區(qū)域暖途,點(diǎn)擊全屏按鈕或設(shè)備橫屏?xí)r才全屏的卑惜,該如何實(shí)現(xiàn)這個(gè)功能呢?

可能你們會(huì)想驻售,我們可以監(jiān)聽(tīng)設(shè)備旋轉(zhuǎn)的通知露久,在設(shè)備旋轉(zhuǎn)時(shí),判斷現(xiàn)在是橫屏還是豎屏欺栗,然后改變view的約束條件(constraint)來(lái)改變大小毫痕。

這里有一個(gè)小技巧,我們只需要設(shè)置好view的長(zhǎng)寬比與屏幕的長(zhǎng)寬比一致就可以了迟几,一切交給 auto layout消请,不用我們?nèi)ゲ傩摹?/p>

// push入這個(gè)控制器的上一個(gè)控制器必須只支持豎屏,不然在手機(jī)橫著時(shí)类腮,push入這個(gè)控制器時(shí)視頻的尺寸有問(wèn)題臊泰。
player.snp.makeConstraints { (make) in
    make.top.equalTo(view.snp.top)
    make.left.equalTo(view.snp.left)
    make.right.equalTo(view.snp.right)
    make.height.equalTo(view.snp.width).multipliedBy(UIScreen.main.bounds.width/UIScreen.main.bounds.height)
    // 寬高比也可以為 16:9
}

注意:

使用上述技巧時(shí),push入這個(gè)控制器的上一個(gè)控制器必須只支持豎屏蚜枢,不然在手機(jī)橫著時(shí)缸逃,push入這個(gè)控制器時(shí)視頻的尺寸有問(wèn)題。

其他方法

本來(lái)還有另外一種方法的厂抽,但只在 iOS 9下有效需频,半年前我用的就是這個(gè)方法。我在寫(xiě)這篇文章時(shí)重新試了下修肠,發(fā)現(xiàn)在iOS 10下已經(jīng)行不通了,也在這里記錄下吧户盯。

AppDelegate 中聲明一個(gè)屬性 allowRotation 來(lái)標(biāo)記是否允許旋轉(zhuǎn)嵌施,實(shí)現(xiàn)如下方法控制橫豎屏:

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    if allowRotation {
        return .landscape
    }
    return .portrait
}

然后在控制器的viewWillAppear方法中饲化,把 allowRotation 設(shè)為 true,這樣界面就會(huì)橫屏顯示了吗伤。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    let appDelegate = UIApplication.shared.delegate as? AppDelegate
    appDelegate?.allowRotation = true
}

這里也有一個(gè)坑吃靠,在view退出前,要把 allowRotation 設(shè)為 false, 不然退出后足淆,之前豎屏顯示的界面會(huì)變成橫屏巢块。

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    let appDelegate = UIApplication.shared.delegate as? AppDelegate
    appDelegate?.allowRotation = false
}

其實(shí)在 iOS 9下還有其他坑的,需要在這個(gè)控制器的界面顯示或消失是精確地修改 allowRotation 的值巧号,比如在該界面橫屏?xí)r彈出豎屏的登錄窗口族奢、在該界面橫屏?xí)r退回來(lái)豎屏的界面等,不然隨時(shí)會(huì)閃退丹鸿,總之就是會(huì)有一些亂七八糟意料之外的錯(cuò)誤出現(xiàn)越走,但現(xiàn)在我的系統(tǒng)已經(jīng)升級(jí)為 iOS 10 了,已經(jīng)無(wú)法重現(xiàn)了靠欢。

這不是一個(gè)好方法廊敌,也不推薦使用!门怪!

總結(jié)

  • 控制橫豎屏切換還是使用系統(tǒng)默認(rèn)的方式最好骡澈,不用去操心子view是否旋轉(zhuǎn),旋轉(zhuǎn)的方向等掷空,也可以避免一些奇奇怪怪的問(wèn)題發(fā)生肋殴,少踩一些坑!拣帽!故推薦方法二疼电。
  • iOS 每升級(jí)一個(gè)版本都會(huì)有一些方法不能用,導(dǎo)致頻頻閃退减拭,巨坑1尾颉!
  • 使用的方法不對(duì)必然會(huì)踩一些莫名其妙的坑拧粪,在此謹(jǐn)記修陡。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市可霎,隨后出現(xiàn)的幾起案子魄鸦,更是在濱河造成了極大的恐慌,老刑警劉巖癣朗,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拾因,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绢记,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)扁达,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蠢熄,你說(shuō)我怎么就攤上這事跪解。” “怎么了签孔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵叉讥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我饥追,道長(zhǎng)图仓,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任判耕,我火速辦了婚禮透绩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壁熄。我一直安慰自己帚豪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布草丧。 她就那樣靜靜地躺著狸臣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昌执。 梳的紋絲不亂的頭發(fā)上烛亦,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音懂拾,去河邊找鬼煤禽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岖赋,可吹牛的內(nèi)容都是我干的檬果。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼唐断,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼选脊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起脸甘,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤恳啥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后丹诀,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體钝的,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翁垂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硝桩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沮峡。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亿柑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棍弄,我是刑警寧澤望薄,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站呼畸,受9級(jí)特大地震影響痕支,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛮原,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一卧须、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧儒陨,春花似錦花嘶、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至笛园,卻和暖如春隘击,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背研铆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工埋同, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棵红。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓凶赁,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親窄赋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哟冬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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

  • iOS 中橫豎屏切換的功能,在開(kāi)發(fā)iOS app中總能遇到忆绰。以前看過(guò)幾次浩峡,感覺(jué)簡(jiǎn)單,但是沒(méi)有敲過(guò)代碼實(shí)現(xiàn)错敢,最近又碰...
    零度_不結(jié)冰閱讀 2,181評(píng)論 0 0
  • IOS 設(shè)備橫豎屏情況 一般情形 所有界面都支持橫豎屏切換如果App的所有切面都要支持橫豎屏的切換翰灾,那只需要勾選【...
    leonardni閱讀 1,815評(píng)論 0 0
  • 關(guān)于橫豎屏適配缕粹,有一句說(shuō)一句,坑挺深的纸淮。之前做Vision和畢設(shè)的時(shí)候就處理過(guò)橫豎屏問(wèn)題平斩,不過(guò)當(dāng)時(shí)的功力太淺,明顯...
    HarwordLiu閱讀 37,251評(píng)論 26 137
  • 現(xiàn)在是深圳12點(diǎn)的夜 窗外時(shí)不時(shí)飄來(lái)落雨的聲音 窸窸窣窣 下下停停 或許這是深圳的氣候特色 又或許這只是夏雨慣用的...
    佛曳香閱讀 145評(píng)論 1 1
  • 可以開(kāi)始寫(xiě)了嗎 這和印象筆記好像沒(méi)區(qū)別 好像還不能加表情包
    知娛之樂(lè)閱讀 184評(píng)論 0 0