开发iOS 程序员iOS

ios在ScrollView中嵌套多个TableView

2015-08-16  本文已影响25116人  EgeTart

说明:示例在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

上一篇下一篇

猜你喜欢

热点阅读