ios在ScrollView中嵌套多个TableView

说明:示例在XCode7 beta5完成

创建一个Single View Application,填写相应的信息. Language选择Swift,把项目存储到一个目录,单击Create完成创建.

Storyboard设计阶段
  1. 打开Main.storyboard文件,取消勾选Use Size Classes,让设计视图呈现iphone的大小.



    下面是取消勾选后,设计面板的样子.



    现在开始在storyboard中添加要使用到的控件.
    从控件库中拖出一个Segmented Control到设计面板的上方.

    选中Segmented Control,给它增加如下约束.



    接下来拖一个ScrollView到到设计面板中,让它铺满剩余的空间.

    同样也要给ScrollView增加约束,这样才可以适应不同屏幕的大小.

    接着拖一个TableView到设计面板中,放置在ScrollView上,成为ScrollView的子视图.

    调整它的大小,让它的大小和ScrollView的相同.

    现在给TableView加上约束.



    此时设计面板中多了一些红色的线条,这说明缺少约束或者约束有冲突.

    通过以下步骤来增加约束,这样storyboard就有足够的信息来确定控件的位置关系.点击Document Outline左上方的红色小圆圈,在点击空心的红色小圆圈,在弹出框中选择Add Missing Constraints.

    接着个体TableView增加一个Prototype Cell.

    然后展开TableView选中Table View Cell,给它设置一个重用ID.

    为了区分接下来要添加的TableView,选中当前的TableView,给它设置一个tag,这里设置为101,再给它取一个名字.

    接下来添加第二个TableView,为了方便操作,在控件库中直接把TableView拖放至Document Outline中,让它位于ScrollView的下方,成为ScrollView的子视图.

    使用相同的方式给刚刚添加的TableView设置一个tag,这里设置为102,给它取一个名字,叫做SecondTableView.再给SecondTableView增加一个Prototype Cell,并设置它的reused identifier为second.
    接下来这一步比较关键,要改变SecondTableView的位置,这样才能给SecondTableView添加上正确的约束.把SecondTableView的x坐标设置为320,完成后,会把SecondTableView移动到设计面板之外,如下所示.

    现在选中SecondTableView为它增加约束.

    此时又出现了红色的线条.没关系,有这个方法让红色线条消失.选中FirstTableView,按住ctrl键,鼠标左键从FirstTableView拖出一条箭头到SecondTableView,松开鼠标在弹出菜单中选择Equal Widths.这样做的结果是,两个TableView具有相同的宽度.

    ok,到此为止,storyboard的设计工作完成,接下来进入代码阶段.

代码阶段

打开辅助视图,为设计面板中的控件生成相应的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改变时,会触发这个Action
    @IBAction func tabChanged(sender: AnyObject) {

    }
}

为了不让Segmented Control给状态栏遮挡,在viewDidLoad()函数的下方,添加以下代码,把状态栏隐藏掉.

// 3. 隐藏状态栏
    override func prefersStatusBarHidden() -> Bool {
        return true
}

让当前ViewController遵循UITableViewDataSource协议,这样就能够给TableView提供数据.类定义的头部将会是下面的样子.
class ViewController: UIViewController, UITableViewDataSource
这里为什么不让ViewController遵循UITableViewDelegate协议呢.因为这个例子只是给TableView填充数据,并不处理发生在TableView上的行为事件.
接着实现两个代理方法,为TableView填充数据.把这两个方法添加在prefersStatusBarHidden()函数的下方.

// 4. 为TableView填充数据
    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,然后使用设置好的reused identifier来获取到cell,给cell的textLabel设置简单的字符串.
在viewDidLoad()函数中,添加以下两行代码.这样TableView的代理方法将会由当前的ViewController来执行.

firstTableView.dataSource = self
secondTableView.dataSource = self

选择一个模拟器,运行一下看有什么效果.



第一个TableView已经呈现出来了,试着往左滑动,把第二个TableView呈现出来.滑了几次,发现不能将SecondTableView呈现出来,为什么会这样呢??难道SecondTableView没有添加到ScrollView中.利用前面添加的Action来做个实验,看是否把SecondTableView添加到了ScrollView中.
首先viewDidLoad()函数的上方,定义一个变量,用来记录ScrollView的内容偏移量.

// 5. 定义一个变量来记录scrollview的内容偏移量
    var offset: CGFloat = 0.0 {
        
        // 当offset的值改变后会执行didSet代码块
        didSet {
            UIView.animateWithDuration(0.3) { () -> Void in
                self.scrollView.contentOffset = CGPoint(x: self.offset, y: 0.0)
            }
        }
    }

didSet代码块的作用是,用一个0.3秒的过度来设置ScrollView的内容偏移量.
接着在 tabChanged(sender: AnyObject) Action中添加以下代码.

// a. 获取到当前item的下标
let index = (sender as! UISegmentedControl).selectedSegmentIndex
// b. 设置scrollview的内容偏移量
offset = CGFloat(index) * self.view.frame.width

item的下标从0开始.因为TableView的宽度和屏幕的宽度相同,所以呈现FirstTableView时ScrollView的内容偏移量为0,呈现SecondTableView时ScrollView的内容偏移量为一个屏幕的宽度,即(self.view.frame.width).
再运行一遍程序,当点击Second item时,可以把SecondTableView呈现出来,说明有把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的内容偏移量
    var offset: CGFloat = 0.0 {
        
        // 当offset的值改变后会执行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改变时,会触发这个Action
    @IBAction func tabChanged(sender: AnyObject) {
        // a. 获取到当前item的下标
        let index = (sender as! UISegmentedControl).selectedSegmentIndex
        
        // b. 设置scrollview的内容偏移量
        offset = CGFloat(index) * self.view.frame.width
    }
    
    // 3. 隐藏状态栏
    override func prefersStatusBarHidden() -> Bool {
        return true
    }
    
    // 4. 为TableView填充数据
    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
    }
}

为什么滑动操作不成功呢,网上看到有人说TableView继承自ScrollView,那么滑动手势很可能在TableView拦截了,因此为ScrollView增加两个滑动手势识别器.
在viewDidLoad()函数中添加以下代码.

// 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()函数添加到最后一个花括号的上方.

// 滑动手势处理函数
    func swipe(gesture: UISwipeGestureRecognizer) {
        
        if gesture.direction == .Left {
            // 向左滑时展示第二个tableview,同时设置选中的segmented item
            offset = self.view.frame.width
            segmented.selectedSegmentIndex = 1
        }
        else {
            offset = 0.0
            segmented.selectedSegmentIndex = 0
        }
    }

0k,代码阶段也结束了.
运行一下程序, 左右滑动可以呈现不同的TableView,选中的segmented item也会跟着改变.
图片加载不出来的话, 这里有pdf格式的,提取码:84e9

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容