原文链接:Swift 实现多个 TableView 的侧滑与切换(模拟 instagram 系列)
模拟 instagram 项目源码:github 仓库:模拟 instagram

关键词:Swift,实现多个 TableView 的侧滑与切换,在 ScrollView 中嵌套多个 TableView,一个页面显示两个 tableview…

目标与成果

如图:

思路

将多个 TableView 放在 ScrollView 里面,将 ScrollView Paging 设置为 Enabled,实现多个 TableView 的侧滑与切换。

上方的 tab 标签跟随 ScrollView.offset.x 进行动画,蓝条为单独绘制的一个长方形 UIView。

通过 UIButton 的 IBAction 中的动画,实现点击 tab 标签滑动到对应 TableView。

解决方案

1. storyboard

添加一个 UIView,高度设置为 30px,上左右与 superview 间距为 0。在里面放置两个 UIButton。将两个 UIButton 组合成 StackView,设置 StackView 上下左右与 superview 间距为 0,设置 distribution 为 Fill Equally。

在 storyboard 创建好 ScrollView 和两个 TableView。在 TableView 中创建好 TableViewCell 模板,并且将对应的类和 reuse identifier 设置好(当然你可以新建 xib 文件,然后在代码里设置 reuse identifier)。两个 TableView 应该是 ScrollView 的子视图。

如果你是在 storyboard 里面给 TableView 添加 Prototype Cells,记得给每个 TableView 添加各自的 Prototype Cells。如果只给其中一个添加 Prototype Cells,那么另一个不会自动添加 reuse identifier。在 cellForRowAtIndexPath 方法中,对未添加 Prototype Cells 的 TableView 调用 dequeReusableCellWithIdentifier 时会报错,因为那个 TableView 本来就没有嘛。

设置 ScrollView 的约束,本项目为下左右间距均为 0,上间距为 1(用于显示上边 tab 标签栏的阴影)。

TableView 的约束不用设置,因为我们要在代码中给他们重新定位。

设置好各个 Prototype Cell 的约束。

2. FourthViewController.swift(对应的 viewController)
全部代码如下:

//
//  FourthViewController.swift
//  Instagram
//
//  Created by Ant on 3/31/16.
//  copyright © 2016 Ant. All rights reserved.
//

import UIKit

class FourthViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,uiscrollviewdelegate {

    @IBOutlet weak var tableViewLeft: UITableView!
    @IBOutlet weak var tableViewRight: UITableView!
    @IBOutlet weak var tabPanel: UIView!
    @IBOutlet weak var followBtn: UIButton!
    @IBOutlet weak var youBtn: UIButton!
    @IBOutlet weak var scrollView: UIScrollView!
    
    let scrollBar = UIView()
    var notifications: [Notification] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建模拟数据
        let notification1 = Notification(from: "CHENGKANG",fromAvatar: UIImage(named: "IMG_8040")!,to: "Deer",toAvatar: UIImage(named: "test")!,type: "like",images: [UIImage(named: "test")!,UIImage(named: "test")!,],date: "3天前")
        let notification2 = Notification(from: "CHENGKANG",images: [UIImage(named: "test")!],date: "3天前")
        
        //添加模拟数据
        self.notifications.append(notification1)
        self.notifications.append(notification2)
        
        //初始化要用到的参数
        let WIDTH = self.view.frame.width
        let HEIGHT = self.view.frame.height - 60 - 30 - 49
        
        //设置 tab 标签面板底部阴影
        self.tabPanel.layer.shadowColor = COLOR_GREY.CGColor
        self.tabPanel.layer.shadowRadius = 0.5
        self.tabPanel.layer.shadowOffset = CGSizeMake(0,0.5)
        self.tabPanel.layer.shadowOpacity = 1
        
        //添加 tab 标签面板底部蓝条
        self.view.addSubview(self.scrollBar)
        self.scrollBar.backgroundColor = COLOR_LIGHT_BLUE
        self.scrollBar.frame = CGRectMake(0,87,WIDTH / 2,3)
        
        //初始化按钮颜色
        self.followBtn.setTitleColor(COLOR_LIGHT_BLUE,forState: .normal)
        
        //设置 scrollView delegate
        self.scrollView.delegate = self
        
        //设置 tableViewLeft delegate,并消除多余分割线
        self.tableViewLeft.delegate = self
        self.tableViewLeft.dataSource = self
        self.tableViewLeft.tableFooterView = UIView()
        
        //设置 tableViewRight delegate,并消除多余分割线
        self.tableViewRight.delegate = self
        self.tableViewRight.dataSource = self
        self.tableViewRight.tableFooterView = UIView()

        //设置 scrollView contentSize
        self.scrollView.contentSize = CGSizeMake(WIDTH * 2,HEIGHT)
        //设置两个 tableView 大小位置
        self.tableViewLeft.frame = CGRectMake(8,WIDTH - 16,HEIGHT)
        self.tableViewRight.frame = CGRectMake(WIDTH + 8,HEIGHT)
    }
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //可以通过判断当前 tableView 是否与某一个 TableView 相同来给定对应内容
//        if tableView == self.tableViewRight {
//            if self.notifications[indexPath.row].images.count == 1 {
//                let cell = LikeWithPicCell()
//                cell.initCell(self.notifications[indexPath.row])
//                return cell
//            } else {
//                let cell = LikeWithPicsCell()
//                cell.initCell(self.notifications[indexPath.row])
//                return cell
//            }
//        }
        
        if self.notifications[indexPath.row].images.count == 1 {
            let cell = tableView.dequeueReusableCellWithIdentifier("LikeWithPicCell") as! LikeWithPicCell
            cell.initCell(self.notifications[indexPath.row])
            return cell
        } else {
            let cell = tableView.dequeueReusableCellWithIdentifier("LikeWithPicsCell") as! LikeWithPicsCell
            cell.initCell(self.notifications[indexPath.row])
            return cell
        }
    }
    
    func tableView(tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
        
        if tableView == self.tableViewRight {
            //此处 -1 是为了让展示内容有区分,因为用的相同数据
            return self.notifications.count - 1
        }
        
        return self.notifications.count
    }
    
    func tableView(tableView: UITableView,heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    
    func tableView(tableView: UITableView,estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    
    func scrollViewDidScroll(scrollView: UIScrollView) {
        //判断当前 scrollView 是我们项目中的 ScrollView 而非那两个 tableView
        if scrollView == self.scrollView {
            //改变 scrollBar x 坐标,达成同步滑动效果。
            let offsetX = scrollView.contentOffset.x
            self.scrollBar.frame = CGRectMake(offsetX / 2,self.view.frame.width / 2,3)
            
            //对应修改 btn 文字颜色
            if offsetX > self.view.frame.width / 2 {
                self.followBtn.setTitleColor(UIColor.blackColor(),forState: .normal)
                self.youBtn.setTitleColor(COLOR_LIGHT_BLUE,forState: .normal)
            } else {
                self.followBtn.setTitleColor(COLOR_LIGHT_BLUE,forState: .normal)
                self.youBtn.setTitleColor(UIColor.blackColor(),forState: .normal)
            }
        }
    }
    @IBAction func followBtnpressed(sender: UIButton) {
        //点击按钮时,通过动画移动到对应 tableView
        UIView.animateWithDuration(0.3,delay: 0,options: [.CurveEaseInOut],animations: { () -> Void in
            self.scrollView.contentOffset.x = 0
            },completion: nil)
    }
    
    @IBAction func youBtnpressed(sender: UIButton) {
        //点击按钮时,通过动画移动到对应 tableView
        UIView.animateWithDuration(0.3,animations: { () -> Void in
            self.scrollView.contentOffset.x = self.view.frame.width
            },completion: nil)
    }

}

在这个项目中,我懒所以两个 TableView 用的相同的 Prototype Cell 和同一组数据,然后在返回第二个 TableView cell 个数时 -1,好在显示时区分二者。

需要注意的几点

  1. 当在一个 ViewController 里面包含多个 TableView 的时候,使用 TableView Delegate 提供的方法时,可以通过判断参数 tableView 是否与项目中对应 TableView 相同来执行对应操作。如上面代码中提到的:

    func tableView(tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
        
        if tableView == self.tableViewRight {
            //此处 -1 是为了让展示内容有区分,因为用的相同数据
            return self.notifications.count - 1
        }
        
        return self.notifications.count
    }
  2. 同上,有多个 scrollView 时,同样方法判定当前是哪个 scrollView。

  3. UITableView 为 UIScrollView 的子类。如果添加了 scrollView delegate,不进行判断的话,可能会出现上下滑动 tableView 时触发 scrollViewDidScroll 方法,导致在第二页时上下滑动时 tab 标签切换到第一个标签。

  4. 不要忘记设置 scrollView.delegate

  5. 不要忘记设置 scrollView.contentSize

  6. 通过设置空白 tableFooterView 为空白 UIView,消除多余分割线。

Swift 实现多个 TableView 的侧滑与切换模拟 instagram 系列的更多相关文章

  1. ios – UITableView和Cell Reuse

    这是我的CustomCell类的init方法解决方法如果没有要显示的图像,则必须清除图像视图:

  2. ios – 如何通过编程方式为UIButtons组设置单个背景图像

    解决方法要使用选择器数组以编程方式创建按钮,您可以使用以下代码片段://定义策略

  3. ios – UIButton背景图像以编程方式更改

    如何在点击事件中更改我的UIButton的背景图像?并使用上一张图片在几秒钟内刷新它?我的意思是在点击后更改它的背景图像并在点击后重置它.解决方法将您的clickEvent图像作为按钮的突出显示图像.

  4. ios – fetchedResultsController.fetchedObjects.count = 0但它充满了对象

    我正在使用相当标准的fetchedResultsController实现来输出tableView.在-viewDidLoad的最后,我正在进行第一次调用:这是我的fetchedResultsController:我的tableView方法:所以,问题是:在_fetchedResultsController.fetchedobjects.count的日志中等于0,但在视觉上tableView充满了对

  5. ios – UIPopoverController出现在错误的位置

    所以我花了一些时间寻找答案,但到目前为止还没有找到任何答案.我正在尝试从UIInputAccessoryView上的按钮呈现弹出窗口.UIBarButtonItem我想显示popover来自定制视图,所以我可以使用图像.我创建这样的按钮:当需要显示popover时,我这样做:但我得到的是:弹出窗口看起来很好,但它应该出现在第一个按钮上时出现在第二个按钮上.然后我发现了这个问题:UIBarButto

  6. ios – UITableView在滚动时阻止重新加载

    或者你能想象一个防止这种行为的好方法吗?解决方法抱歉,我没有足够的声誉来添加评论,因此在单独的答案中回答您的上一个问题.-performSelector:withObject:afterDelay:延迟为0.0秒不会立即执行给定的选择器,而是在当前的RunloopCycle结束后和给定的延迟之后执行它.-performSelector:withObject:添加到当前Runloop循环中并执行.这与直接调用该方法相同.因此,使用-performSelector:withObject:afterDelay:

  7. ios – 在Swift中通过标记访问UITableViewCell内部的不同视图

    我正在尝试使用swift为iOS8制作应用程序.这里的目标是制作一种新闻源.此Feed显示来自用户的帖子,其遵循特定模式.我想过使用UITableView,其中每个单元格都遵循自定义布局.当我尝试访问其中的文本标签时出现问题.我尝试通过它的标签访问它,但是当我这样做时,整个应用程序崩溃了.报告的错误是“Swift动态转换失败”,我使用以下代码访问视图:难道我做错了什么?解决方法我认为问题是标签0.所有视图都是默认值0.所以尝试另一个标签值.

  8. ios – 如何实现`prepareForReuse`?

    解决方法尝试将此添加到您的MGSwipeTableCell.m:

  9. ios – 在UITableView上轻扫以删除以使用UIPanGestureRecognizer

    我使用以下代码将UIPanGuestureRecognizer添加到整个视图中:在主视图中我有一个UITableView,它有这个代码来启用滑动删除功能:只有RUNNING1打印到日志中,并且“删除”按钮不会显示.我相信其原因是UIPanGestureRecognizer,但我不确定.如果这是正确的,我该如何解决这个问题.如果这不正确,请提供原因并解决.谢谢.解决方法从document:Ifage

  10. viewWillAppear vs Viewdidload ios

    使用iOS导航应用程序的代码时,我遇到了麻烦:我在哪里可以为UITableView设置方法“initdata”?请帮帮我.解决方法您可以根据应用程序的需求放置initData,如果您的表需要每次使用新数据加载数据,那么它应该在否则,如果表需要通过单个数据重新加载,该数据不会发生变化或者没有对数据执行任何编辑操作,则应使用

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部