Skip to content

XCode 快捷键

  • 运行:cmd + R
  • 打开组件库:cmd + shift + L
  • 缩小模拟器:cmd + m
  • 点击控件,按住 ctrl 拖到代码区,生成一个绑定

操作

添加绑定、改变绑定的变量名称

添加绑定,参考 XCode 快捷键

改变绑定的变量名称,要先移除绑定,再重新生成绑定

批量添加同一个绑定

控制器之间如何通信

父传子

子通知父

兄弟通信

祖孙通信

路由

A -> B

使用 UIViewController 里面定义的 prepare 方法,进行相关操作

点击某个组件,按住 ctrl 拖到对应的页面,它会显示相应的 Action Segue,让你选择用什么方式显示跳转页面。

当我们想知道跳转的是哪个页面,可以设置“箭头”组件的 identifier,然后在 ViewContrller 声明方法:

swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destination.
    // Pass the selected object to the new view controller.
    if segue.identifier == "selectCity" {
        // 获取对应 View 的实例
        let vc = segue.destination as! SelectCityViewController
    }
}

当我们想传值的时候,可以先在目标页面设置对应的变量,然后在跳转时,会触发 prepare 方法,根据 segue.identifier 知道要跳转的页面,用 segue.destination 获取目标页面的 Controller 的实例,再进行赋值即可

B 返回 A

B 返回 A 只需执行 dismiss 方法,就会销毁当前 Controller,回到之前的页面。但我们要传值,需要定义 delegate

B 页面:

swift
// 先声明协议
procotol BviewDelegate {
  var delegate: SelectCityDelegate?
}
class BController: UIViewController {
  func hahTest(name: String)
  
  @IBAction func test() {
    delegate?.hahTest(name: "Batman")
  }
}

A 页面:

swift
// 注入对应的 delegate
class ViewController: UIViewController, BviewDelegate {
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
        if segue.identifier == "b" {
          let vc = segue.destination as! BController
            // 当去 B 页面的时候,要将目标属性的 delegate 设为当前页面的
            vc.delegate = self
        }
    }
  
  func test(name: String) {}
}

简单来说,就是在 A 跳到 B 页面时,将 A 页面实例赋值到 B 页面中,B 页面触发 A 实例预设的方法进行传值

前进刷新,返回缓存

顶部导航操作按钮的控制

代码控制跳转

要跳转的 controller 配置 StoryboardID

swift
// 创建对应控制器的页面实例
let view = self.storyboard?.instantiateViewController(withIdentifier: String(describing: type(of: LoginController())))
            as! LoginController
navigationController?.pushViewController(view, animated: true)

上拉下拉功能

使用 GTMRefresh 库实现

该库要配合 UITableView 使用

伪代码:

swift
//
//  HomeControllerTableViewController.swift
//  drg-app
//
//  Created by Apple on 2021/2/20.
//  Copyright © 2021 Apple. All rights reserved.
//

import UIKit
import SwiftyJSON
import AlamofireImage
import GTMRefresh

class TableViewController: UITableViewController {
    
    var currentPage = 1
    var pageSize = 3
    var count = 0
    var isEnd = false
    var isRefresh = false
    var list: [HomeItem] = []

    override func viewDidLoad() {
        super.viewDidLoad()
      	// 自动计算 cell 高度
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 100
        // 隐藏间隔线
        tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
        // 设置底部高度,不然底部会有一部分内容被挡住;默认底部内容的高度是 60
        tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 60))

        getData()
        
        // 下拉刷新
        self.tableView.gtm_addRefreshHeaderView {
            self.currentPage = 1
            self.isEnd = false
          	// 清空数据
            self.list.removeAll()
            self.count = 0
            // 重渲 tableView 数据
            self.tableView.reloadData()
            self.isRefresh = true
            self.getData()
        }
        
        // 上拉加载
        self.tableView.gtm_addLoadMoreFooterView {
            if (self.isEnd) {
                return
            }
            self.currentPage += 1
            self.getData()
        }
    }

    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell: HomeItemCell?
        cell = tableView.dequeueReusableCell(withIdentifier: "homeItem", for: indexPath) as? HomeItemCell
        // 解决 tableView 内容重叠问题
        if cell == nil {
            let stackView = list[indexPath.row]
            cell = HomeItemCell(style: .default, reuseIdentifier: "cell")
            cell!.frame.size.width = tableView.frame.width
            cell!.initContent(target: stackView)
        }
        return cell!
    }

    func getData() {
        crequest(url: "/api/web/content",
                 params: ["pageSize": pageSize, "currentPage": currentPage],
                 callback: { res in
                    if res.success {
                        let data = res.data as! JSON
                        if (self.currentPage * self.pageSize) >= data["totalElements"].intValue {
                            self.isEnd = true
                        }
                        // add...
                        self.count = self.list.count
                    }
                    if self.isRefresh {
                        self.isRefresh = false
                        self.tableView.endRefreshing(isSuccess: true)
                        self.tableView.reloadData()
                    } else {
                        self.tableView.endLoadMore(isNoMoreData: self.isEnd)
                        self.tableView.reloadData()
                    }
        })
    }

}

请求

使用第三方库:Alamofire 4.x 版本

使用 swiftyJSON 库进行 json 处理:SwiftyJSON

使用 SVProgressHUD 实现全局提示:SVProgressHUD

拦截器实现

没找到该库拦截器方法,通过封装函数实现拦截器效果

swift
public func crequest(url: String,
                     method: HTTPMethod = .get,
                     params: [String: Any]? = nil,
                     callback: @escaping(_ res: RequestResult) -> Void = { res in }
) -> DataRequest {
    SVProgressHUD.show()
    return Alamofire.request("http://localhost:7000\(url)",
        method: method,
        parameters: params,
        encoding: method == .get ? URLEncoding.default : JSONEncoding.default)
        .response { response in
            var result = RequestResult()
            let json = JSON(response.data!)
            // 处理 http 异常码
            if response.response?.statusCode != 200 {
                result.msg = response.error?.localizedDescription ?? "系统繁忙"
                result.data = json["errorMsg"]
                result.success = false
            // 处理业务异常码
            } else if (json["code"].stringValue != "00000000") {
                result.msg = json["msg"].stringValue
                result.data = json["result"]
                result.success = false
            } else {
                result.data = json["result"]
            }
            SVProgressHUD.dismiss()
            callback(result)
    }
}

使用:

swift
func getData() {
  crequest(url: '/api', params: [...], callback: {
    res in
    if (res.success) {
      // res.data
    }
	})
}

按这布局进行划分

内存缓存

如何缓存一份可共同使用的对象

本地缓存

如何模拟实现 localStorage 和 sessionStorage

文件

本地文件读写操作

设备权限

相机、拍照

文件

app 间跳转

剪贴板

地理位置

提示

使用 SVProgressHUD 实现全局提示:SVProgressHUD

全局提示

确认框

loading

接入第三方 api

微信

支付宝

微博分享

TODOS

  1. request 模仿 js 的 async 写法
  2. Navigation + tabbar 布局,非 tabbar 的页面,如何跳转到某个 tabbar 的页面?