iOS14開發(fā)-UIView

介紹

  • UIView 會占用屏幕上一個矩形的空間。
  • 主要處理兩件事:畫出矩形控件引镊,并處理其中的事件胸嘁。
  • UIView 是層級結(jié)構(gòu),UIView 只有一個父 View凹蜂,但可以有多個子 View馍驯。子 View 的順序和子 View 返回的數(shù)組中的位置有關(guān)(storyboard 中左側(cè)的樹形結(jié)構(gòu)圖中的先后順序)。
  • UIView 可以直接在 storyboard 里面拖拽使用玛痊,也可以使用純代碼方式使用汰瘫。

UILabel、UITextField擂煞、UIButton

UILabel

  • 顯示靜態(tài)文本混弥。
  • 文字換行
    • 使用 storyboard:設(shè)置Lines為 0,然后在Text中用option+回車換行对省。
    • 使用代碼:label.numberOfLines = 0蝗拿,設(shè)置文字的時候用\n換行。

UITextField

  • 輸入框蒿涎。
  • 框內(nèi)左邊視圖
textField.leftView = UIImageView(image: UIImage(systemName: "phone"))
textField.leftViewMode = .always
  • 橫線式輸入框
class ViewController: UIViewController {
    @IBOutlet var textfield: UITextField!

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

    // 自動布局時放這里
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        // 設(shè)置無邊框
        textfield.borderStyle = .none
        // 調(diào)用
        textfield.setBottomBorder(UIColor.red, 1)
    }
}

extension UITextField {
    func setBottomBorder(_ color: UIColor, _ lineWidth: Int) {
        // 創(chuàng)建一個層
        let bottomBorder = CALayer()
        let lineWidth = CGFloat(lineWidth)
        bottomBorder.borderColor = color.cgColor
        // 設(shè)置層的frame
        bottomBorder.frame = CGRect(x: 0, y: frame.size.height - lineWidth, width: frame.size.width, height: frame.size.height)
        // 設(shè)置寬度
        bottomBorder.borderWidth = lineWidth
        // 插入
        layer.addSublayer(bottomBorder)
        // 裁剪
        layer.masksToBounds = true
    }
}
  • 設(shè)置提示文字顏色
// 使用NSAttributedString
textField.attributedPlaceholder = NSAttributedString(string: "請輸入信息", attributes: [.foregroundColor : UIColor.red])

UIButton

  • 按鈕蛹磺,最重要的是點擊事件。
  • 文字換行
    • 使用 storyboard:設(shè)置 Lines Break 為Word Wrap同仆,然后在 title 中用option+回車換行萤捆。
    • 使用代碼:titleLabel.lineBreakMode = NSLineBreakByWordWrapping;,設(shè)置文字的時候用\n換行。

登錄案例

class ViewController: UIViewController {
    @IBOutlet var username: UITextField!
    @IBOutlet var password: UITextField!

    @IBAction func loginBtnClicked(_ sender: Any) {
        let uname = username.text
        let upwd = password.text

        // 可以在這里對輸入的信息進行判斷
        print("用戶名為:\(uname!), 密碼為:\(upwd!)")
    }

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

    // 觸摸屏幕方法
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 退鍵盤的方式之一
        view.endEditing(true)
    }
}

UITextView

  • 多行文本輸入框俗或。
  • 使用類似 UITextField市怎。
  • 內(nèi)容可滾動。

UIImageView

  • 圖片控件

Tom貓案例

class ViewController: UIViewController {
    var imageArray: [UIImage] = [UIImage]()
    @IBOutlet var tomcat: UIImageView!

    @IBAction func drink(_ sender: Any) {
        imageArray.removeAll()
        
        var imgName = ""
        // 1.加載drink的動畫圖片
        for index in 0 ... 80 {
            // drink_XX.jpg
            imgName = "drink_\(index).jpg"
            // 通過名字構(gòu)造一張圖片
            let image = UIImage(named: imgName)
            imageArray.append(image!)
        }

        // 2.讓圖片進行動畫的播放
        // 圖片數(shù)組
        tomcat.animationImages = imageArray
        // 動畫時間
        tomcat.animationDuration = 3.0
        // 動畫次數(shù)
        tomcat.animationRepeatCount = 1
        // 開始動畫
        tomcat.startAnimating()
    }

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

UISwitch辛慰、UISlider区匠、UIStepper 、UISegmentControl

class ViewController: UIViewController {
    @IBOutlet var light: UIImageView!
    @IBOutlet var voice: UIImageView!
    @IBOutlet var product: UILabel!
    @IBOutlet var flower: UIImageView!

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

    // sender 誰觸發(fā)這個事件 就將誰傳進來
    @IBAction func valueChanged(_ sender: Any) {
        // UISwitch
        let switchUI = sender as? UISwitch

        if let switchUI = switchUI {
            if switchUI.isOn {
                light.image = UIImage(named: "light.png")
            } else {
                light.image = UIImage(named: "nomal.png")
            }
        }

        // UISlider
        let slider = sender as? UISlider

        if let slider = slider {
            if slider.value < 0.3 {
                voice.image = UIImage(named: "low.png")
            } else if slider.value < 0.7 {
                voice.image = UIImage(named: "middle.png")
            } else {
                voice.image = UIImage(named: "high.png")
            }
        }

        // UIStepper
        let stepper = sender as? UIStepper

        if let stepper = stepper {
            let value = stepper.value
            if value < stepper.maximumValue {
                product.text = "您購買了\(Int(value))件商品"
            }
            if value == stepper.minimumValue {
                product.text = "您未購買任何商品"
            }
        }

        // UISegmentedControl
        let segment = sender as? UISegmentedControl

        if let segment = segment {
            if segment.selectedSegmentIndex == 0 {
                flower.image = UIImage(named: "red.png")
            } else if segment.selectedSegmentIndex == 1 {
                flower.image = UIImage(named: "purple.png")
            }
        }
    }
}

思考:湯姆貓和本案例帅腌,事件都是相同的驰弄,那么能否用一個 IBAction 完成?

UIActivityIndicatorView速客、UIProgressView

  • UIActivityIndicatorView:無進度的進度條戚篙。
  • UIProgressView:有進度的進度條。
class ViewController: UIViewController {
    @IBOutlet var indicator: UIActivityIndicatorView!

    @IBOutlet var progressView: UIProgressView!

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

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        indicator.stopAnimating()

        // UIView動畫
        // 動畫執(zhí)行的時間
        // 動畫執(zhí)行的操作
        UIView.animate(withDuration: 5.0) {
            // 千萬不要直接設(shè)置progress溺职,因為這樣是不會有動畫效果的
            // self.progressView.progress = 1.0

            // 必須要用帶animated參數(shù)的方法來進行設(shè)置 才會有動畫
            self.progressView.setProgress(1.0, animated: true)
        }
    }
}

UIDatePicker

  • 日期選擇器
class ViewController: UIViewController {
    @IBOutlet var birthday: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        let datePicker = UIDatePicker(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 300))
        datePicker.datePickerMode = .dateAndTime
        // 當(dāng)控件datePicker發(fā)生valueChanged事件時 會調(diào)用target的action方法
        datePicker.addTarget(self, action: #selector(getBirthday), for: .valueChanged)
        birthday.inputView = datePicker
    }

    @objc func getBirthday(datePicker: UIDatePicker) {
        // 獲取日期
        let date = datePicker.date
        // 日期格式化
        // 2018.10.17 2018/10/17 2018-10-17 2018年10月17日
        let dateFormatter = DateFormatter()
        // 24小時制岔擂,hh小寫12小時制
        dateFormatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
        // 賦值給birthday
        birthday.text = dateFormatter.string(from: date)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 退鍵盤的另外一種方式
        birthday.resignFirstResponder()
    }
}
  • iOS 14 新增了卡片式日期選擇器,且成為默認(rèn)樣式浪耘。如果需要顯示成滾輪模式乱灵,需要手動設(shè)置:
datePicker.preferredDatePickerStyle = .wheels

注意:需要在 frame 之前設(shè)置。

  • 給輸入框的 inputView 設(shè)置 UIDatePicker七冲。

UIPickerView

  • 選擇器控件
    • 數(shù)據(jù)源(DataSource)
    • 代理(Delegate)
    • 可以通過代碼和拽線的方式設(shè)置數(shù)據(jù)源和代理痛倚。
class ViewController: UIViewController {
    let province: [String] = ["安徽", "浙江", "江蘇", "山東", "河南", "湖北"]
    let city: [String] = ["合肥", "杭州", "南京", "濟南", "鄭州", "武漢", "廈門", "長沙"]

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

extension ViewController: UIPickerViewDataSource {
    // UIPickerViewDataSource
    // 返回選擇器的列數(shù)
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0 {
            return province.count
        } else {
            return city.count
        }
    }
}

extension ViewController: UIPickerViewDelegate {
    // UIPickerViewDelegate
    // 該方法會調(diào)用多次 根據(jù)numberOfRowsInComponent的返回值決定
    // 每一次調(diào)用就應(yīng)該返回一個數(shù)據(jù) 它會自動從第0行開始設(shè)置title
    // 6行 0 1 2 3 4 5
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if component == 0 {
            return province[row]
        } else {
            return city[row]
        }
    }

    // 設(shè)置UIView
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        return UIImageView(image: UIImage(systemName: "person"))
    }

    // 設(shè)置高度
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 44
    }

    // 選擇的數(shù)據(jù)列(滾動的時候調(diào)用)
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if component == 0 {
            print(province[row])
        } else {
            print(city[row])
        }
    }
}

說明:

  1. titleForRow方法在代理方法里而不是在數(shù)據(jù)源方法里。
  2. 內(nèi)容除了設(shè)置 String 類型澜躺,還可以設(shè)置 UIView 類型蝉稳,且一旦設(shè)置了 UIView,設(shè)置 String 的失效苗踪。
  3. 代理方法可以設(shè)置內(nèi)容的高度。

數(shù)據(jù)聯(lián)動

在某一列滾動的時候削锰,重新設(shè)置聯(lián)動列的顯示數(shù)據(jù)通铲,然后進行刷新操作。

UIScrollView器贩、UIPageControl

UIScrollView

  • 滾動控件
  • 三個重要屬性
    • contentSize:UIScrollView 滾動的范圍颅夺。
    • contentOffset:UIScrollView 當(dāng)前顯示區(qū)域的頂點相對于內(nèi)容左上角的偏移量(滾動到了什么位置)。
    • contentInset:ScrollView的內(nèi)容相對于 UIScrollView 的上下左右的留白蛹稍。

UIPageControl

  • 頁面指示器
  • 一般配合 UIScrollView 分頁使用吧黄。

圖片輪播

class ViewController: UIViewController {
    // 屏幕寬度
    let bannerW = UIScreen.main.bounds.size.width
    // 高度
    let bannerH = 260
    var banner: UIScrollView!
    var pageControl: UIPageControl!

    override func viewDidLoad() {
        super.viewDidLoad()
        // 設(shè)置UIScrollView
        setupBanner()
        // 設(shè)置UIPageControl
        setupPageControl()
    }

    func setupBanner() {
        banner = UIScrollView(frame: CGRect(x: 0, y: 0, width: Int(bannerW), height: bannerH))
        // 是否顯示滾動條
        banner.showsHorizontalScrollIndicator = false
        // 是否需要彈簧效果
        banner.bounces = false
        // 是否分頁
        banner.isPagingEnabled = true
        // 代理
        banner.delegate = self
        // 添加圖片
        for i in 0 ... 4 {
            // x坐標(biāo)
            let w = Int(bannerW) * i
            // 圖片控件
            let img = UIImageView(frame: CGRect(x: w, y: 0, width: Int(bannerW), height: bannerH))
            img.image = UIImage(named: "img_\(i)")
            banner.addSubview(img)
        }
        // 設(shè)置contentSize
        banner.contentSize = CGSize(width: bannerW * 5.0, height: 0)

        view.addSubview(banner)
    }

    func setupPageControl() {
        pageControl = UIPageControl(frame: CGRect(x: 0, y: 0, width: 200, height: 20))
        // 指示器的顏色
        pageControl.pageIndicatorTintColor = UIColor.red
        // 當(dāng)前頁的顏色
        pageControl.currentPageIndicatorTintColor = UIColor.cyan
        // 總頁數(shù)
        pageControl.numberOfPages = 5
        pageControl.center = CGPoint(x: bannerW * 0.5, y: 240.0)
        // 監(jiān)聽事件
        pageControl.addTarget(self, action: #selector(pageIndicate), for: .valueChanged)

        view.addSubview(pageControl)
    }

    @objc func pageIndicate(pageControl: UIPageControl) {
        let currentIndex = pageControl.currentPage
        // 設(shè)置偏移
        banner.setContentOffset(CGPoint(x: Int(bannerW) * currentIndex, y: 0), animated: true)
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // 獲取contentOffset
        let contentOffset = scrollView.contentOffset
        // 獲取索引
        let index = contentOffset.x / bannerW
        // 設(shè)置當(dāng)前頁
        pageControl.currentPage = Int(index)
    }
}

UITableView

  • 表視圖,是 iOS 開發(fā)中最重要的 UI 控件之一唆姐。
  • 整體結(jié)構(gòu)
    • 一個 UITableView 由 Header + 多個 Section + Footer 組成拗慨。
    • 一個 Section 由 Header + 多個 Row + Footer 組成。
    • 一個 Row 就是 UITableViewCell。
  • UITableViewCell結(jié)構(gòu)
    • 里面有一個contentView赵抢,顯示的內(nèi)容放在上面剧蹂。
    • contentView里默認(rèn)有 3 個控件:2 個UILabel、1一個UIImageView烦却,并由此產(chǎn)生了四種不同的 UITableViewCell 的顯示樣式宠叼。
  • 類似 PickerView,需要提供數(shù)據(jù)源以顯示數(shù)據(jù)其爵。

基本使用

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

extension ViewController: UITableViewDataSource {
    // 有多少分組
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    // 每個分組中有多少行
    func tableView(_ tableView: UITableView, numberOfRowsInSection section
        : Int) -> Int {
        return 20
    }

    // 每一行的內(nèi)容
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "abc")
        // default 只顯示textLabel和imageView
        // subtitle value1 三個都顯示
        // value2 只顯示textLabel和detailTextLabel
        cell.textLabel?.text = "AAA"
        cell.detailTextLabel?.text = "BBB"
        cell.imageView?.image = UIImage(named: "setting_about_pic")
        return cell
    }
}

UITableViewCell重用

  • 重用原理
  • 重用好處
  • 重用標(biāo)識符
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

extension ViewController: UITableViewDataSource {
    // 有多少分組
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    // 一個分組中有多少行
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }

    // 每一行長什么樣
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        /**
         // 純代碼實現(xiàn)復(fù)用
         // 去重用池子中找cell
         var cell = tableView.dequeueReusableCell(withIdentifier: "abc")
         // 池子中沒有就創(chuàng)建一個新的
         if cell == nil {
             cell = UITableViewCell(style: .subtitle, reuseIdentifier: "abc")
         }
         cell?.textLabel?.text = "AAA"
         cell?.detailTextLabel?.text = "BBB"
         cell?.imageView?.image = UIImage(named: "setting_about_pic")

         return cell!
           */

        // SB方式實現(xiàn)復(fù)用
        let cell = tableView.dequeueReusableCell(withIdentifier: "abc")
        cell?.textLabel?.text = "AAA"
        cell?.detailTextLabel?.text = "BBB"
        cell?.imageView?.image = UIImage(named: "setting_about_pic")

        return cell!
    }
}

數(shù)據(jù)源

數(shù)據(jù)不再固定冒冬,而是由外界提供,多使用數(shù)組摩渺。

class ViewController: UIViewController {
    var content: Array<String>?
    var detailContent: Array<String>?

    override func viewDidLoad() {
        super.viewDidLoad()

        content = ["iPhone 4", "iPhone 4s", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6 Plus", "iPhone 6s", "iPhone 6s Plus", "iPhone 7", "iPhone 7 Plus", "iPhone 8", "iPhone 8 Plus", "iPhone X", "iPhone Xs", "iPhone XR", "iPhone Xs Max", "iPhone 11", "iPhone 11 Pro", "iPhone 11 Pro Max", "iPhone 12 mini", "iPhone 12", "iPhone 12 Pro", "iPhone 12 Pro Max"]

        detailContent = ["iPhone 4 - iOS 4", "iPhone 4s - iOS 5", "iPhone 5 - iOS 6", "iPhone 5s - iOS 7", "iPhone 6 - iOS 8", "iPhone 6 Plus - iOS 8", "iPhone 6s - iOS 9", "iPhone 6s Plus - iOS 9", "iPhone 7 - iOS 10", "iPhone 7 Plus - iOS 10", "iPhone 8 - iOS 11", "iPhone 8 Plus - iOS 11", "iPhone X - iOS 11", "iPhone Xs - iOS 12", "iPhone XR - iOS 12", "iPhone Xs Max - iOS 12", "iPhone 11 - iOS 13", "iPhone 11 Pro - iOS 13", "iPhone 11 Pro Max - iOS 13", "iPhone 12 mini - iOS 14", "iPhone 12 - iOS 14", "iPhone 12 Pro - iOS 14", "iPhone 12 Pro Max - iOS 14"]
    }
}

extension ViewController: UITableViewDataSource {
    // 有多少分組
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    // 一個分組中有多少行
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return content!.count
    }

    // 每一行長什么樣
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // SB方式實現(xiàn)復(fù)用
        let cell = tableView.dequeueReusableCell(withIdentifier: "ABC")
        cell?.textLabel?.text = content?[indexPath.row]
        cell?.detailTextLabel?.text = detailContent?[indexPath.row]
        cell?.imageView?.image = UIImage(named: "iPhone")

        return cell!
    }
}

代理

class ViewController: UIViewController {
    var content: Array<String>?
    var detailContent: Array<String>?

    override func viewDidLoad() {
        super.viewDidLoad()

        content = ["iPhone 4", "iPhone 4s", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6 Plus", "iPhone 6s", "iPhone 6s Plus", "iPhone 7", "iPhone 7 Plus", "iPhone 8", "iPhone 8 Plus", "iPhone X", "iPhone Xs", "iPhone XR", "iPhone Xs Max", "iPhone 11", "iPhone 11 Pro", "iPhone 11 Pro Max", "iPhone 12 mini", "iPhone 12", "iPhone 12 Pro", "iPhone 12 Pro Max"]

        detailContent = ["iPhone 4 - iOS 4", "iPhone 4s - iOS 5", "iPhone 5 - iOS 6", "iPhone 5s - iOS 7", "iPhone 6 - iOS 8", "iPhone 6 Plus - iOS 8", "iPhone 6s - iOS 9", "iPhone 6s Plus - iOS 9", "iPhone 7 - iOS 10", "iPhone 7 Plus - iOS 10", "iPhone 8 - iOS 11", "iPhone 8 Plus - iOS 11", "iPhone X - iOS 11", "iPhone Xs - iOS 12", "iPhone XR - iOS 12", "iPhone Xs Max - iOS 12", "iPhone 11 - iOS 13", "iPhone 11 Pro - iOS 13", "iPhone 11 Pro Max - iOS 13", "iPhone 12 mini - iOS 14", "iPhone 12 - iOS 14", "iPhone 12 Pro - iOS 14", "iPhone 12 Pro Max - iOS 14"]
    }
}

extension ViewController: UITableViewDataSource {
    // 有多少分組
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    // 一個分組中有多少行
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return content!.count
    }

    // 每一行長什么樣
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // SB方式實現(xiàn)復(fù)用
        let cell = tableView.dequeueReusableCell(withIdentifier: "ABC")
        cell?.textLabel?.text = content?[indexPath.row]
        cell?.detailTextLabel?.text = detailContent?[indexPath.row]
        cell?.imageView?.image = UIImage(named: "iPhone")

        return cell!
    }
}

extension ViewController: UITableViewDelegate {
    // Section頭部
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "iPhone 大全"
    }

    // Section尾部
    func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
        return "iOS大全"
    }

    // 選中(點擊行)
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let contentText = content?[indexPath.row]
        let detailText = detailContent?[indexPath.row]
        print("\(contentText!)--\(detailText!)")
    }

    // 行高
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80.0
    }

    // Section頭部高
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 100.0
    }

    // Section尾部高
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 60.0
    }
}

編輯

class ViewController: UIViewController {
    @IBOutlet var tableView: UITableView!
    var content: Array<String>?
    var detailContent: Array<String>?

    override func viewDidLoad() {
        super.viewDidLoad()

        content = ["iPhone 4", "iPhone 4s", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6 Plus", "iPhone 6s", "iPhone 6s Plus", "iPhone 7", "iPhone 7 Plus", "iPhone 8", "iPhone 8 Plus", "iPhone X", "iPhone Xs", "iPhone XR", "iPhone Xs Max", "iPhone 11", "iPhone 11 Pro", "iPhone 11 Pro Max", "iPhone 12 mini", "iPhone 12", "iPhone 12 Pro", "iPhone 12 Pro Max"]

        detailContent = ["iPhone 4 - iOS 4", "iPhone 4s - iOS 5", "iPhone 5 - iOS 6", "iPhone 5s - iOS 7", "iPhone 6 - iOS 8", "iPhone 6 Plus - iOS 8", "iPhone 6s - iOS 9", "iPhone 6s Plus - iOS 9", "iPhone 7 - iOS 10", "iPhone 7 Plus - iOS 10", "iPhone 8 - iOS 11", "iPhone 8 Plus - iOS 11", "iPhone X - iOS 11", "iPhone Xs - iOS 12", "iPhone XR - iOS 12", "iPhone Xs Max - iOS 12", "iPhone 11 - iOS 13", "iPhone 11 Pro - iOS 13", "iPhone 11 Pro Max - iOS 13", "iPhone 12 mini - iOS 14", "iPhone 12 - iOS 14", "iPhone 12 Pro - iOS 14", "iPhone 12 Pro Max - iOS 14"]
    }

    @IBAction func edit(_ sender: Any) {
        tableView.setEditing(true, animated: true)
    }

    @IBAction func done(_ sender: Any) {
        tableView.setEditing(false, animated: true)
    }
}

extension ViewController: UITableViewDataSource {
    // 有多少分組
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    // 一個分組中有多少行
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return content!.count
    }

    // 每一行長什么樣
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // SB方式實現(xiàn)復(fù)用
        let cell = tableView.dequeueReusableCell(withIdentifier: "ABC")
        cell?.textLabel?.text = content?[indexPath.row]
        cell?.detailTextLabel?.text = detailContent?[indexPath.row]
        cell?.imageView?.image = UIImage(named: "iPhone")

        return cell!
    }
}

extension ViewController: UITableViewDelegate {
    // 允許編輯
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    // 提交編輯
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // 1.刪除數(shù)組中對應(yīng)的數(shù)據(jù)
            content?.remove(at: indexPath.row)
            detailContent?.remove(at: indexPath.row)
            // 2.TableView顯示的那一樣刪除
            tableView.deleteRows(at: [indexPath], with: .automatic)
        } else if editingStyle == .insert {
            // 1.增加一條數(shù)據(jù)
            content?.insert("iPhone 1", at: indexPath.row)
            detailContent?.insert("iPhone 1 - iPhone OS", at: indexPath.row)
            // 2.增加一行
            tableView.insertRows(at: [indexPath], with: .automatic)
        }
    }

    // 刪除按鈕的文字
    func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
        return "刪除"
    }

    // 編輯的風(fēng)格(默認(rèn)是刪除)
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        return .insert
    }

    // 能否移動
    func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    // 移動表格
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        // 內(nèi)容
        let contentText = content?[sourceIndexPath.row]
        // 先刪除
        content?.remove(at: sourceIndexPath.row)
        // 再增加
        content?.insert(contentText!, at: destinationIndexPath.row)
        // 詳情操作和內(nèi)容一樣
        let detailContentText = detailContent?[sourceIndexPath.row]
        detailContent?.remove(at: sourceIndexPath.row)
        detailContent?.insert(detailContentText!, at: destinationIndexPath.row)
    }
}

索引

class ViewController: UIViewController {
    @IBOutlet var tableView: UITableView!
    var sectionTitles: [String] = ["A", "C", "F", "G", "H", "M", "S", "T", "X", "Z"]
    var contentsArray: [[String]] = [
        ["阿偉", "阿姨", "阿三"],
        ["陳晨", "成龍", "陳鑫", "陳丹"],
        ["芳仔", "房祖名", "方大同", "范偉"],
        ["郭靖", "郭美美", "過兒", "郭襄"],
        ["何仙姑", "和珅", "郝歌", "何仙姑"],
        ["馬云", "毛不易"],
        ["孫周", "沈冰", "史磊"],
        ["陶也", "淘寶", "圖騰"],
        ["項羽", "夏紫薇", "許巍", "許晴"],
        ["祝枝山", "周杰倫", "張柏芝"],
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.sectionIndexBackgroundColor = UIColor.red
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return sectionTitles.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return contentsArray[section].count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "abc")
        cell?.textLabel?.text = contentsArray[indexPath.section][indexPath.row]
        return cell!
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sectionTitles[section]
    }
}

extension ViewController: UITableViewDelegate {
    // 索引的標(biāo)題
    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return sectionTitles
    }

    // 點擊索引
    func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
        // 點擊的索引標(biāo)題
        print(title)
        // 一定要返回index 否則 點擊索引不會自動滾動到指定位置
        return index
    }
}

自定義UITableViewCell

用 3 種自定義 Cell 的方式分別實現(xiàn)下面的案例:

  • iPhone 信息展示
  • 新聞列表

下拉刷新

class ViewController: UIViewController {
    @IBOutlet var tableView: UITableView!
    var content: Array<String> = ["iPhone 4", "iPhone 4s", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6 Plus", "iPhone 6s", "iPhone 6s Plus", "iPhone 7", "iPhone 7 Plus", "iPhone 8", "iPhone 8 Plus", "iPhone X", "iPhone Xs", "iPhone XR", "iPhone Xs Max", "iPhone 11", "iPhone 11 Pro", "iPhone 11 Pro Max", "iPhone 12 mini", "iPhone 12", "iPhone 12 Pro", "iPhone 12 Pro Max"]
    var detailContent: Array<String> = ["iPhone 4 - iOS 4", "iPhone 4s - iOS 5", "iPhone 5 - iOS 6", "iPhone 5s - iOS 7", "iPhone 6 - iOS 8", "iPhone 6 Plus - iOS 8", "iPhone 6s - iOS 9", "iPhone 6s Plus - iOS 9", "iPhone 7 - iOS 10", "iPhone 7 Plus - iOS 10", "iPhone 8 - iOS 11", "iPhone 8 Plus - iOS 11", "iPhone X - iOS 11", "iPhone Xs - iOS 12", "iPhone XR - iOS 12", "iPhone Xs Max - iOS 12", "iPhone 11 - iOS 13", "iPhone 11 Pro - iOS 13", "iPhone 11 Pro Max - iOS 13", "iPhone 12 mini - iOS 14", "iPhone 12 - iOS 14", "iPhone 12 Pro - iOS 14", "iPhone 12 Pro Max - iOS 14"]

    override func viewDidLoad() {
        super.viewDidLoad()
        // 創(chuàng)建UIRefreshControl
        let refresh = UIRefreshControl()
        // 設(shè)置顯示的標(biāo)題
        refresh.attributedTitle = NSAttributedString(string: "下拉刷新")
        // 設(shè)置下拉事件
        refresh.addTarget(self, action: #selector(loadData), for: .valueChanged)
        // 放到tableView的頭部
        tableView.refreshControl = refresh
    }

    @objc func loadData() {
        // 延遲執(zhí)行
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            // 增加一條數(shù)據(jù)
            self.content.insert("iPhone 3GS", at: 0)
            self.detailContent.insert("iPhone 3GS - iOS 3", at: 0)
            // 刷新表格 結(jié)束刷新的狀態(tài)
            self.tableView.reloadData()
            // 停止刷新
            if (self.tableView.refreshControl?.isRefreshing)! {
                self.tableView.refreshControl?.endRefreshing()
            }
        }
    }
}

extension ViewController: UITableViewDataSource {
    // 一個分組中有多少行
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return content.count
    }

    // 每一行長什么樣
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // SB方式實現(xiàn)復(fù)用
        let cell = tableView.dequeueReusableCell(withIdentifier: "ABC")
        cell?.textLabel?.text = content[indexPath.row]
        cell?.detailTextLabel?.text = detailContent[indexPath.row]
        cell?.imageView?.image = UIImage(named: "iPhone")

        return cell!
    }
}

靜態(tài)單元格

  • 需要使用 UITableViewController简烤。
  • 直接在 storyboard 中布局,不需要使用數(shù)據(jù)源方法证逻,但如果需要使用到代理方法乐埠,仍然需要在控制器中實現(xiàn)相應(yīng)的方法。
  • 適用于基本不需要動態(tài)修改囚企、布局固定的頁面丈咐,如個人中心、設(shè)置等龙宏。
  • 微信“發(fā)現(xiàn)”界面案例棵逊。

UICollectionView

  • 集合視圖,是 iOS 開發(fā)中最重要的 UI 控件之一银酗。
  • 整體結(jié)構(gòu)
    • 一個 UICollectionView 由 Header + 多個 Section + Footer 組成辆影。
    • 一個 Section 由 Header + 多個 Item + Footer 組成。
    • 一個 Item 就是 UICollectionViewCell黍特。
  • 類似 UITableView蛙讥,需要提供數(shù)據(jù)源以顯示數(shù)據(jù)。
  • 支持 Diffable Data Source灭衷,類為 UICollectionViewDiffableDataSource次慢,使用方式類似 UITableViewDiffableDataSource。

UICollectionViewFlowLayout

與 UITableView 不同翔曲,UICollectionView 需要提供布局參數(shù)迫像,常用的有UICollectionViewFlowLayout,通過它可以設(shè)置內(nèi)容的大小瞳遍、間距和方向等信息闻妓。

class ViewController: UIViewController {
    @IBOutlet var collectionView: UICollectionView!
    let screenW = UIScreen.main.bounds.size.width
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 布局
        let layout = UICollectionViewFlowLayout()        
        // 設(shè)置item大小
        layout.itemSize = CGSize(width: (screenW - 15.0) * 0.5, height: 212)
        // item之間的間距
        layout.minimumInteritemSpacing = 5.0
        // 行間距
        layout.minimumLineSpacing = 5.0
        // 組邊距
        layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
        // 滾動方向
        layout.scrollDirection = .vertical

        collectionView.collectionViewLayout = layout
    }
}

extension ViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "abc", for: indexPath)

        return cell
    }
}

extension ViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("\(indexPath.row)")
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市掠械,隨后出現(xiàn)的幾起案子由缆,更是在濱河造成了極大的恐慌注祖,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犁功,死亡現(xiàn)場離奇詭異氓轰,居然都是意外死亡,警方通過查閱死者的電腦和手機浸卦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門署鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人限嫌,你說我怎么就攤上這事靴庆。” “怎么了怒医?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵炉抒,是天一觀的道長。 經(jīng)常有香客問我稚叹,道長焰薄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任扒袖,我火速辦了婚禮塞茅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘季率。我一直安慰自己野瘦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布飒泻。 她就那樣靜靜地躺著鞭光,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泞遗。 梳的紋絲不亂的頭發(fā)上惰许,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音史辙,去河邊找鬼汹买。 笑死屹耐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旺上。 我是一名探鬼主播璃俗,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钮糖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纵潦,失蹤者是張志新(化名)和其女友劉穎徐鹤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邀层,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡返敬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了寥院。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劲赠。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖秸谢,靈堂內(nèi)的尸體忽然破棺而出凛澎,到底是詐尸還是另有隱情,我是刑警寧澤估蹄,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布塑煎,位于F島的核電站,受9級特大地震影響臭蚁,放射性物質(zhì)發(fā)生泄漏最铁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一垮兑、第九天 我趴在偏房一處隱蔽的房頂上張望冷尉。 院中可真熱鬧,春花似錦甥角、人聲如沸网严。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽震束。三九已至,卻和暖如春当犯,著一層夾襖步出監(jiān)牢的瞬間垢村,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工嚎卫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘉栓,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓拓诸,卻偏偏與公主長得像侵佃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子奠支,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

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