ios實(shí)現(xiàn)底部PopupWindow(底部彈出菜單)

前言


在Android中要實(shí)現(xiàn)底部彈出菜單很容易诀豁,有專門(mén)的PopupWindow類胎源,我們只需要用xml訂制好其內(nèi)容View以及設(shè)置其彈出位置即可鲫寄,非常容易耻卡。但是疯汁,在ios中就不能這么直接了,沒(méi)有現(xiàn)成的東西卵酪,需要自己想辦法來(lái)實(shí)現(xiàn)幌蚊。

思路分析


  1. 反正最終一定要實(shí)現(xiàn)效果,那么內(nèi)容View一定要解決掉凛澎,那么是在Interface Builder編輯實(shí)現(xiàn)還是直接用代碼實(shí)現(xiàn)呢霹肝?答案是都可以估蹄,但為了方便和訂制相對(duì)比較規(guī)范塑煎,建議用interface Builder編輯。
  2. 內(nèi)容ok了臭蚁,那么內(nèi)容放在哪里最铁?這是個(gè)核心問(wèn)題,也就是確定PopupWindow的容器垮兑。我們知道ios視圖的層級(jí)結(jié)構(gòu)是Window->RootView->各種組件冷尉。顯然PopupWindow要么放在Window中要么放在RootView中,但是如果放在RootView勢(shì)必會(huì)影響RootView中原來(lái)的組件系枪,而且與PopupWindow這個(gè)名字也不相符雀哨。所以,理想的容器就是Window私爷。
  3. 如何彈出的問(wèn)題雾棺,其實(shí)這個(gè)比較好解決,彈出時(shí)就把PopupWindow加入到容器衬浑,消失時(shí)就把它從容器中移除捌浩。要實(shí)現(xiàn)從底部彈出,從底部消失的效果工秩,只需要借助UIView動(dòng)畫(huà)尸饺,變換起始坐標(biāo)就可以了进统,比較容易。

具體實(shí)現(xiàn)


UI

用Interface Builder實(shí)現(xiàn)浪听,ViewController直接選用UIViewController螟碎,內(nèi)部選的是UICollectionView方便動(dòng)態(tài)更新,當(dāng)然這個(gè)根據(jù)需要隨意迹栓。布局用AutoLayout就不用多說(shuō)了抚芦,比較簡(jiǎn)單。直接上圖:



注意此ViewController的RootView就是我們需要添加到Window的view迈螟,為了效果叉抡,將其背景色置為clearcolor。將其中交互的組件右鍵拖拽到PopupWindow類形成映射答毫。

彈出

將RootView添加到Window中褥民,并顯示在最前面。直接上代碼:

 func create()-> PopupWindow {
        let window = UIApplication.shared.keyWindow
        window?.addSubview(self.view)
        window?.bringSubview(toFront: self.view)
        self.view.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        UIView.animate(withDuration: 0.3) {
            animation in
            self.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        }
        return self
    }

這里關(guān)鍵就是addSubView方法添加到Window洗搂,bringSubView顯示到前臺(tái)消返。UIView動(dòng)畫(huà)將view的y坐標(biāo)由屏幕高度改變?yōu)?,從而實(shí)現(xiàn)由底部彈出效果耘拇。
這里返回自身對(duì)象是為了方便鏈?zhǔn)皆O(shè)置組件屬性和其他屬性撵颊。

添加交互功能

雖然現(xiàn)在已經(jīng)可以彈出PopupWindow了,但并不具有交互功能惫叛。并且我們?yōu)榱吮阌趶?fù)用倡勇,不會(huì)把交互的功能直接寫(xiě)在PopupWindow中,而是根據(jù)需要寫(xiě)在調(diào)用它的地方嘉涌。這里有兩種方式:

  • 以協(xié)議的方式妻熊,把方法寫(xiě)在協(xié)議中,調(diào)用部分實(shí)現(xiàn)這個(gè)協(xié)議并重寫(xiě)回調(diào)函數(shù)仑最,這和Android的接口基本一致扔役。
  • 以函數(shù)作為參數(shù)類型的方式,調(diào)用部分通過(guò)傳遞函數(shù)類型參數(shù)至PopupWindow警医,而在調(diào)用部分以閉包或者尾隨閉包的形式添加交互功能亿胸。

兩種方式一般都可以隨性,但第一種適合交互函數(shù)比較多的時(shí)候预皇。第二種適合于同一調(diào)用類中出現(xiàn)多個(gè)地方不同調(diào)用侈玄,一些設(shè)置屬性也不相同。

我們這里選擇第一種深啤,以協(xié)議的方式:

 protocol PopupWindowDelegate {
    func attach()
    func detach()
    func rename()
    func delete()
    func control()
}

這里具體函數(shù)完全不用管它拗馒,是從項(xiàng)目中截取的。
當(dāng)然我們需要在PopupWindow中定義一個(gè)該協(xié)議類型的變量:

public var delegate: PopupWindowDelegate?

通過(guò)協(xié)議對(duì)象來(lái)調(diào)用交互函數(shù):

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let row = indexPath.row
        switch itemsString[row] {
            case "attach":
            delegate?.attach()
            cancel()
            case "detach":
            delegate?.detach()
            cancel()
            case "rename":
            delegate?.rename()
            cancel()
            case "delete":
            delegate?.delete()
            cancel()
            case "control":
            delegate?.control()
            cancel()
        default:
            break
        }
    }

這是UICollectionView item的選擇函數(shù)溯街,這里不多說(shuō)诱桂。注意協(xié)議對(duì)象對(duì)其函數(shù)的調(diào)用洋丐,這里只相當(dāng)于一種綁定。真正的調(diào)用在調(diào)用地方對(duì)協(xié)議對(duì)象的賦值挥等。

除了這些還有一個(gè)最重要的東西友绝,就是聲明對(duì)于PopupWindow對(duì)象的一個(gè)強(qiáng)引用,如果這個(gè)不存在肝劲,交互功能依然不可用迁客。原因是為了防止當(dāng)前對(duì)象被回收掉,有了強(qiáng)引用辞槐,只有強(qiáng)引用置空時(shí)掷漱,對(duì)象才能被回收掉。

var strongSelf: PopupWindow?

引用賦值即可以放在彈出函數(shù)create()中榄檬,也可以放在viewDidLoad()中卜范,執(zhí)行順序是彈出函數(shù)create()在前。這里放在viewDidLoad()中的:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.view.backgroundColor = UIColor.init(white: 0, alpha: 0)
        let gesture = UITapGestureRecognizer(target: self, action: #selector(cancel))
        gesture.delegate = self
        self.dismissView.addGestureRecognizer(gesture)
        self.collectionView.delegate = self
        self.collectionView.dataSource = self
        strongSelf = self
    }

里面對(duì)于UICollectionView的操作可以忽略鹿榜,dismissView是取消PopupView的按鈕海雪,當(dāng)然并沒(méi)有用UIButton,用的是UIView舱殿,所以要手動(dòng)添加點(diǎn)擊事件奥裸。

取消

取消PopupWindow比較簡(jiǎn)單,將view從其容器中移除沪袭,并將其強(qiáng)引用置空湾宙。為了實(shí)現(xiàn)從底部消失的效果,仍然用UIView動(dòng)畫(huà)變換y坐標(biāo)實(shí)現(xiàn)枝恋。

func cancel() {
        UIView.animate(withDuration: 0.3) {
            animation in
            self.view.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        }
        DispatchQueue.main.asyncAfter(deadline: .now()+0.3) {
            self.view.removeFromSuperview()
        }
        strongSelf = nil
    }
調(diào)用

在調(diào)用類中實(shí)現(xiàn)PopupWindowDelegate協(xié)議创倔,重寫(xiě)交互函數(shù)嗡害。創(chuàng)建PopupWindow對(duì)象焚碌,并設(shè)置委托屬性和其他屬性。

let popupWindow = UIStoryboard(name: "DefiniteUI", bundle: nil).instantiateViewController(withIdentifier: "popup") as! PopupWindow
popupWindow.delegate = self
popupWindow.create().setItems(value: items)

效果

彈出PopWindow:



取消PopWindow:


后記


舉一反三霸妹,除了PopupWindow十电,類似的各種自定義的Dialog都可以這樣去實(shí)現(xiàn),讀者可以去試試叹螟。
另外鹃骂,初次寫(xiě)文章,水平較差罢绽,讀者請(qǐng)隨意批評(píng)指教畏线,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末良价,一起剝皮案震驚了整個(gè)濱河市寝殴,隨后出現(xiàn)的幾起案子蒿叠,更是在濱河造成了極大的恐慌,老刑警劉巖蚣常,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件市咽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡抵蚊,警方通過(guò)查閱死者的電腦和手機(jī)施绎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贞绳,“玉大人谷醉,你說(shuō)我怎么就攤上這事「员眨” “怎么了孤紧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵凌受,是天一觀的道長(zhǎng)谐算。 經(jīng)常有香客問(wèn)我古沥,道長(zhǎng)蛆楞,這世上最難降的妖魔是什么赦肋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任啸箫,我火速辦了婚禮耳舅,結(jié)果婚禮上震叙,老公的妹妹穿的比我還像新娘羹应。我一直安慰自己揽碘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布园匹。 她就那樣靜靜地躺著雳刺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裸违。 梳的紋絲不亂的頭發(fā)上掖桦,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音供汛,去河邊找鬼枪汪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛怔昨,可吹牛的內(nèi)容都是我干的雀久。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼趁舀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赖捌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起矮烹,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤越庇,失蹤者是張志新(化名)和其女友劉穎奋隶,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體悦荒,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唯欣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搬味。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片境氢。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碰纬,靈堂內(nèi)的尸體忽然破棺而出萍聊,到底是詐尸還是另有隱情,我是刑警寧澤悦析,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布寿桨,位于F島的核電站,受9級(jí)特大地震影響强戴,放射性物質(zhì)發(fā)生泄漏亭螟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一骑歹、第九天 我趴在偏房一處隱蔽的房頂上張望预烙。 院中可真熱鬧,春花似錦道媚、人聲如沸扁掸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谴分。三九已至,卻和暖如春镀脂,著一層夾襖步出監(jiān)牢的瞬間牺蹄,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工狗热, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钞馁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓匿刮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親探颈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熟丸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評(píng)論 25 707
  • 翻譯自“View Controller Programming Guide for iOS”。 1 彈出視圖控制器...
    lakerszhy閱讀 3,513評(píng)論 2 20
  • 李欣芮 四年級(jí) 蔬菜王國(guó)和水果王國(guó)開(kāi)始了一場(chǎng)大戰(zhàn)伪节。各個(gè)水果用出自己的特殊攻擊光羞,把蔬菜們打得遍體鱗傷...
    我在江上飛閱讀 570評(píng)論 1 4
  • 后來(lái)我才明白,喜歡一個(gè)人到骨子里面潜慎,就是你不會(huì)找他捡多,不會(huì)再有期待,不會(huì)想他每天在干嘛铐炫,你會(huì)在工作很累很累的時(shí)候垒手,點(diǎn)...
    一見(jiàn)妮就笑閱讀 601評(píng)論 0 0
  • 中午的大太陽(yáng),投射在皮膚上倒信,灼熱 就是這樣的夏季 再多喝一口冰水就能涼到心里去 所有令我皺眉的事都有你 所有令你開(kāi)...
    柏熙汐閱讀 164評(píng)論 0 0