ios在ScrollView中嵌套多個TableView

說明:示例在XCode7 beta5完成

創(chuàng)建一個Single View Application,填寫相應(yīng)的信息. Language選擇Swift,把項目存儲到一個目錄,單擊Create完成創(chuàng)建.

Storyboard設(shè)計階段
  1. 打開Main.storyboard文件,取消勾選Use Size Classes,讓設(shè)計視圖呈現(xiàn)iphone的大小.



    下面是取消勾選后,設(shè)計面板的樣子.



    現(xiàn)在開始在storyboard中添加要使用到的控件.
    從控件庫中拖出一個Segmented Control到設(shè)計面板的上方.

    選中Segmented Control,給它增加如下約束.



    接下來拖一個ScrollView到到設(shè)計面板中,讓它鋪滿剩余的空間.

    同樣也要給ScrollView增加約束,這樣才可以適應(yīng)不同屏幕的大小.

    接著拖一個TableView到設(shè)計面板中,放置在ScrollView上,成為ScrollView的子視圖.

    調(diào)整它的大小,讓它的大小和ScrollView的相同.

    現(xiàn)在給TableView加上約束.



    此時設(shè)計面板中多了一些紅色的線條,這說明缺少約束或者約束有沖突.

    通過以下步驟來增加約束,這樣storyboard就有足夠的信息來確定控件的位置關(guān)系.點擊Document Outline左上方的紅色小圓圈,在點擊空心的紅色小圓圈,在彈出框中選擇Add Missing Constraints.

    接著個體TableView增加一個Prototype Cell.

    然后展開TableView選中Table View Cell,給它設(shè)置一個重用ID.

    為了區(qū)分接下來要添加的TableView,選中當前的TableView,給它設(shè)置一個tag,這里設(shè)置為101,再給它取一個名字.

    接下來添加第二個TableView,為了方便操作,在控件庫中直接把TableView拖放至Document Outline中,讓它位于ScrollView的下方,成為ScrollView的子視圖.

    使用相同的方式給剛剛添加的TableView設(shè)置一個tag,這里設(shè)置為102,給它取一個名字,叫做SecondTableView.再給SecondTableView增加一個Prototype Cell,并設(shè)置它的reused identifier為second.
    接下來這一步比較關(guān)鍵,要改變SecondTableView的位置,這樣才能給SecondTableView添加上正確的約束.把SecondTableView的x坐標設(shè)置為320,完成后,會把SecondTableView移動到設(shè)計面板之外,如下所示.

    現(xiàn)在選中SecondTableView為它增加約束.

    此時又出現(xiàn)了紅色的線條.沒關(guān)系,有這個方法讓紅色線條消失.選中FirstTableView,按住ctrl鍵,鼠標左鍵從FirstTableView拖出一條箭頭到SecondTableView,松開鼠標在彈出菜單中選擇Equal Widths.這樣做的結(jié)果是,兩個TableView具有相同的寬度.

    ok,到此為止,storyboard的設(shè)計工作完成,接下來進入代碼階段.

代碼階段

打開輔助視圖,為設(shè)計面板中的控件生成相應(yīng)的outlet.同時為Segmented Control綁定一個Action,它的事件類型為Value Changed,可以把Segmented Control上的items當作獨立的button來使用.



完成后,代碼文件會類似于這樣.

import UIKit
class ViewController: UIViewController {
    // 1. 拖拽生成控件的outlet
    @IBOutlet weak var segmented: UISegmentedControl!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var firstTableView: UITableView!
    @IBOutlet weak var secondTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
       
    }

    // 2. 當Segmented Control選擇的item改變時,會觸發(fā)這個Action
    @IBAction func tabChanged(sender: AnyObject) {

    }
}

為了不讓Segmented Control給狀態(tài)欄遮擋,在viewDidLoad()函數(shù)的下方,添加以下代碼,把狀態(tài)欄隱藏掉.

// 3. 隱藏狀態(tài)欄
    override func prefersStatusBarHidden() -> Bool {
        return true
}

讓當前ViewController遵循UITableViewDataSource協(xié)議,這樣就能夠給TableView提供數(shù)據(jù).類定義的頭部將會是下面的樣子.
class ViewController: UIViewController, UITableViewDataSource
這里為什么不讓ViewController遵循UITableViewDelegate協(xié)議呢.因為這個例子只是給TableView填充數(shù)據(jù),并不處理發(fā)生在TableView上的行為事件.
接著實現(xiàn)兩個代理方法,為TableView填充數(shù)據(jù).把這兩個方法添加在prefersStatusBarHidden()函數(shù)的下方.

// 4. 為TableView填充數(shù)據(jù)
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var reusedID: String!
        
        if tableView.tag == 101 {
            reusedID = "first"
        }
        else {
            reusedID = "second"
        }
        
        let cell = tableView.dequeueReusableCellWithIdentifier(reusedID) as UITableViewCell!
        
        if tableView.tag == 101 {
            cell.textLabel!.text = "第一個TableView"
        }
        else {
            cell.textLabel!.text = "第二個TableView"
        }
        
        return cell
    }

這里利用tag來判斷是哪一個TableView,然后使用設(shè)置好的reused identifier來獲取到cell,給cell的textLabel設(shè)置簡單的字符串.
在viewDidLoad()函數(shù)中,添加以下兩行代碼.這樣TableView的代理方法將會由當前的ViewController來執(zhí)行.

firstTableView.dataSource = self
secondTableView.dataSource = self

選擇一個模擬器,運行一下看有什么效果.



第一個TableView已經(jīng)呈現(xiàn)出來了,試著往左滑動,把第二個TableView呈現(xiàn)出來.滑了幾次,發(fā)現(xiàn)不能將SecondTableView呈現(xiàn)出來,為什么會這樣呢??難道SecondTableView沒有添加到ScrollView中.利用前面添加的Action來做個實驗,看是否把SecondTableView添加到了ScrollView中.
首先viewDidLoad()函數(shù)的上方,定義一個變量,用來記錄ScrollView的內(nèi)容偏移量.

// 5. 定義一個變量來記錄scrollview的內(nèi)容偏移量
    var offset: CGFloat = 0.0 {
        
        // 當offset的值改變后會執(zhí)行didSet代碼塊
        didSet {
            UIView.animateWithDuration(0.3) { () -> Void in
                self.scrollView.contentOffset = CGPoint(x: self.offset, y: 0.0)
            }
        }
    }

didSet代碼塊的作用是,用一個0.3秒的過度來設(shè)置ScrollView的內(nèi)容偏移量.
接著在 tabChanged(sender: AnyObject) Action中添加以下代碼.

// a. 獲取到當前item的下標
let index = (sender as! UISegmentedControl).selectedSegmentIndex
// b. 設(shè)置scrollview的內(nèi)容偏移量
offset = CGFloat(index) * self.view.frame.width

item的下標從0開始.因為TableView的寬度和屏幕的寬度相同,所以呈現(xiàn)FirstTableView時ScrollView的內(nèi)容偏移量為0,呈現(xiàn)SecondTableView時ScrollView的內(nèi)容偏移量為一個屏幕的寬度,即(self.view.frame.width).
再運行一遍程序,當點擊Second item時,可以把SecondTableView呈現(xiàn)出來,說明有把SecondTableView加到ScrollView中.



到這個階段的完整代碼如下.

import UIKit
class ViewController: UIViewController, UITableViewDataSource {
    // 1. 拖拽生成控件的outlet
    @IBOutlet weak var segmented: UISegmentedControl!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var firstTableView: UITableView!
    @IBOutlet weak var secondTableView: UITableView!
    
    // 5. 定義一個變量來記錄scrollview的內(nèi)容偏移量
    var offset: CGFloat = 0.0 {
        
        // 當offset的值改變后會執(zhí)行didSet代碼塊
        didSet {
            UIView.animateWithDuration(0.3) { () -> Void in
                self.scrollView.contentOffset = CGPoint(x: self.offset, y: 0.0)
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
       
        firstTableView.dataSource = self
        secondTableView.dataSource = self
        
    }
    
    // 2. 當Segmented Control選擇的item改變時,會觸發(fā)這個Action
    @IBAction func tabChanged(sender: AnyObject) {
        // a. 獲取到當前item的下標
        let index = (sender as! UISegmentedControl).selectedSegmentIndex
        
        // b. 設(shè)置scrollview的內(nèi)容偏移量
        offset = CGFloat(index) * self.view.frame.width
    }
    
    // 3. 隱藏狀態(tài)欄
    override func prefersStatusBarHidden() -> Bool {
        return true
    }
    
    // 4. 為TableView填充數(shù)據(jù)
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var reusedID: String!
        
        if tableView.tag == 101 {
            reusedID = "first"
        }
        else {
            reusedID = "second"
        }
        
        let cell = tableView.dequeueReusableCellWithIdentifier(reusedID) as UITableViewCell!
        
        if tableView.tag == 101 {
            cell.textLabel!.text = "第一個TableView"
        }
        else {
            cell.textLabel!.text = "第二個TableView"
        }
        return cell
    }
}

為什么滑動操作不成功呢,網(wǎng)上看到有人說TableView繼承自ScrollView,那么滑動手勢很可能在TableView攔截了,因此為ScrollView增加兩個滑動手勢識別器.
在viewDidLoad()函數(shù)中添加以下代碼.

// 6. 為scrollView增加滑動手勢識別器
        let swipeLeft = UISwipeGestureRecognizer(target: self, action: "swipe:")
        swipeLeft.direction = .Left
        swipeLeft.numberOfTouchesRequired = 1
        
        let swipeRight = UISwipeGestureRecognizer(target: self, action: "swipe:")
        swipeRight.direction = .Right
        swipeRight.numberOfTouchesRequired = 1
        
        scrollView.addGestureRecognizer(swipeLeft)
        scrollView.addGestureRecognizer(swipeRight)

定義了兩個滑動的手勢識別器,方向分別向左和向右,numberOfTouchesRequired的意思是只要單點觸摸就可以完成滑動操作.
把swipe()函數(shù)添加到最后一個花括號的上方.

// 滑動手勢處理函數(shù)
    func swipe(gesture: UISwipeGestureRecognizer) {
        
        if gesture.direction == .Left {
            // 向左滑時展示第二個tableview,同時設(shè)置選中的segmented item
            offset = self.view.frame.width
            segmented.selectedSegmentIndex = 1
        }
        else {
            offset = 0.0
            segmented.selectedSegmentIndex = 0
        }
    }

0k,代碼階段也結(jié)束了.
運行一下程序, 左右滑動可以呈現(xiàn)不同的TableView,選中的segmented item也會跟著改變.
圖片加載不出來的話, 這里有pdf格式的,提取碼:84e9

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容