Swift实现热门搜索和历史记录

作者: 萤火驻守心间 | 来源:发表于2022-01-30 12:00 被阅读0次
    IMG_0179.PNG

    HistoryView:
    1、布局热门搜索和历史搜索
    2、动态计算文本的frame,区分换行和不换行
    3、触发事件传递到viewModel,并接收回调更新UI
    SearchView:
    1、布局搜索框和右侧搜索按钮
    2、触发事件传递到viewModel
    SearchViewModel:
    1、跟踪view的时间,处理historyArr数据
    2、公开属性和方法让view保持最新状态

    存储历史搜索记录到缓存,可用NSUserDefault、归档、plist、sqlite等,此处采用的是UserDefault

    完整代码为
    ViewController.swift

    class ViewController: UIViewController {
        var viewModel = SearchViewModel()
        lazy var searchv: SearchView = {
           let search = SearchView(frame: CGRect(x: 0, y: 0, width: kscreenw, height: kNavBarH))
            search.backgroundColor = .orange
            return search
        }()
        lazy var historyv: HistoryView = {
           let history = HistoryView(frame: CGRect(x: 0, y: kNavBarH, width: kscreenw, height: kscreenh-kNavBarH))
            history.backgroundColor = .white
            return history
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            view.backgroundColor = .white
            view.addSubview(searchv)
            view.addSubview(historyv)
            searchv.viewModel = viewModel
            historyv.viewModel = viewModel
            weak var weakself = self
            viewModel.searchHistoryBlock = {
                searchStr in
                print("搜索的内容",searchStr)
                weakself?.navigationController?.pushViewController(SecViewController(), animated: true)
            }
        }
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            navigationController?.setNavigationBarHidden(true, animated: false)
        }
        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
            navigationController?.setNavigationBarHidden(false, animated: false)
            viewModel.saveHistoryArr()
        }
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            view.endEditing(true)
        }
    }
    

    SearchView.swift

    class SearchView: UIView,UITextFieldDelegate {
        
        var viewModel:SearchViewModel?
        //闭包传值
        var returnSearchContent : ((String)->())?
        lazy var textfield: UITextField = {
            let tfield = UITextField.init(frame: CGRect(x: 30, y: kSafeHeightTopBar+0.5*(kNavBarH-kSafeHeightTopBar-30), width: kscreenw-120, height: 30))
            tfield.backgroundColor = .white
            tfield.placeholder = "请输入搜索内容"
            tfield.layer.cornerRadius = 0.5*30
            tfield.layer.masksToBounds = true
            tfield.font = .systemFont(ofSize: 14)
            tfield.returnKeyType = .search
            tfield.delegate = self
            tfield.clearButtonMode = .whileEditing
            let leftv = UIView(frame: CGRect(x: 0, y:0, width: 10, height: 30))
            tfield.leftView = leftv
            tfield.leftViewMode = .always
            return tfield
        }()
        lazy var searchBtn : UIButton = {
            let btn = UIButton()
            btn.frame = CGRect(x: kscreenw-60, y: kSafeHeightTopBar, width: 40, height: kNavBarH-kSafeHeightTopBar)
            btn.setTitle("搜索", for: .normal)
            btn.setTitleColor(.white, for: .normal)
            btn.addTarget(self, action: #selector(searchBtnTap(btn:)), for: .touchUpInside)
            return btn
        }()
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(textfield)
            addSubview(searchBtn)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        /*
         搜索按钮点击
         */
        @objc func searchBtnTap(btn: UIButton) {
            textfield.resignFirstResponder()
            viewModel?.startSearch(text: textfield.text ?? "")
        }
        //点击键盘搜索时调用
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            textfield.resignFirstResponder()
            viewModel?.startSearch(text: textfield.text ?? "")
            return true
        }
        
    }
    

    HistoryView.swift

    class HistoryView: UIView {
        lazy var hotLabel: UILabel = {
            let label = UILabel(frame: CGRect(x: 20, y: 30, width: 100, height: 40))
            label.text = "热门搜索"
            label.textColor = .colorWithHexString(color: "333333")
            label.font = .systemFont(ofSize: 15)
            return label
        }()
        let hotView = UIView()
        lazy var hisLabel: UILabel = {
            let label = UILabel()
            label.text = "历史搜索"
            label.textColor = .colorWithHexString(color: "333333")
            label.font = .systemFont(ofSize: 15)
            return label
        }()
        lazy var deletBtn : UIButton = {
            let btn = UIButton()
            btn.setImage(.init(named: "delet"), for: .normal)
            btn.addTarget(self, action: #selector(deletHistory), for: .touchUpInside)
            return btn
        }()
        let historyv = UIView()
        
        private var temModel = SearchViewModel()
        var viewModel:SearchViewModel{
            set {
                temModel = newValue
                weak var weakself = self
                temModel.updateHistoryV = {
                    () in
                    weakself?.updateHistoryView()
                }
            }
            get {
                temModel
            }
        }
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(hotLabel)
            addSubview(hotView)
            //添加热门搜索
            let hotArr = ["桂花","紫薇花","月季","玫瑰","茉莉花","紫茉莉","锦带花","长春花","梅花"]
            let temheight = addLabelWithArr(bgView: hotView, arr: hotArr, tag: 1000)
            hotView.frame = CGRect(x: 0, y: hotLabel.frame.maxY+10, width: kscreenw, height: temheight)
            hisLabel.frame = CGRect(x: 20, y: hotView.frame.maxY+10, width: 100, height: 40)
            deletBtn.frame = CGRect(x: kscreenw-60, y: hisLabel.frame.origin.y, width: 40, height: 40)
            //历史搜索
            addSubview(hisLabel)
            addSubview(deletBtn)
            addSubview(historyv)
            historyv.frame = CGRect(x: 0, y: hisLabel.frame.maxY+10, width: kscreenw, height: 0)
            updateHistoryView()
        }
        
        func updateHistoryView() {
            for label in historyv.subviews {
                if label.tag == 1001 {
                    label.removeFromSuperview()
                }
            }
            let historyh = addLabelWithArr(bgView: historyv, arr: viewModel.historyArr, tag: 1001)
            historyv.frame = CGRect(x: 0, y: hisLabel.frame.maxY+10, width: kscreenw, height: historyh)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        func addLabelWithArr(bgView: UIView, arr: [String], tag: Int) -> CGFloat{
            if arr.count < 1 {
                return 0
            }
            var orgSize = CGSize(width: 20, height: 0)
            for i in 0..<arr.count {
                let str = arr[i]
                let label = UILabel()
                label.tag = tag
                label.textColor = .white
                label.font = .systemFont(ofSize: 16)
                label.backgroundColor = .orange
                label.layer.masksToBounds = true
                label.layer.cornerRadius = 14
                label.textAlignment = .center
                label.text = str
                orgSize = label.nextSizeAndCurFrame(size: orgSize)
                bgView.addSubview(label)
                label.isUserInteractionEnabled = true
                let tap: UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(labelClick(tap:)))
                label.addGestureRecognizer(tap)
            }
            let totalh = orgSize.height+40
            return totalh
        }
        
        @objc func labelClick(tap: UITapGestureRecognizer) {
            let label = tap.view as? UILabel
            viewModel.startSearch(text: label?.text)
        }
        @objc func deletHistory() {
            viewModel.clearhistory()
        }
        deinit {
            print("historyView deinit")
        }
        
    }
    
    private extension UILabel {
        func nextSizeAndCurFrame(size: CGSize) -> CGSize{
            let strSize = text?.getStringSize(font: font, viewSize: CGSize(width: kscreenw-40, height: CGFloat(MAXFLOAT)))
            let orW = size.width
            var labelw: CGFloat = 0
            if let wid = strSize?.width {
                labelw = wid + 20
            }
            let spaceX: CGFloat = 20
            let spaceY: CGFloat = 10
            let labelh: CGFloat = 26
            var newSize = CGSize.zero
            if orW+labelw > kscreenw-40 {
                //换行
                frame = CGRect(x: spaceX, y: size.height+labelh+spaceY, width: labelw, height: labelh)
                //2*spaceX+labelw代表换行后label左侧间距spacex+右侧间距也是spaxex+labelw
                newSize = CGSize(width: 2*spaceX+labelw, height: size.height+labelh+spaceY)
                
            }else {
                frame = CGRect(x: orW, y: size.height, width: labelw, height: labelh)
                newSize = CGSize(width: orW+spaceX+labelw, height: size.height)
            }
            return newSize
        }
    }
    
    private extension String {
        //获取文字的宽高
        func getStringSize(font: UIFont, viewSize: CGSize) -> CGSize {
            let rect = self.boundingRect(with: viewSize, options: [.usesLineFragmentOrigin,.truncatesLastVisibleLine,.usesFontLeading], attributes: [NSAttributedString.Key.font: font], context: nil)
            return rect.size
        }
    }
    

    SearchViewModel.swift

    class SearchViewModel: NSObject {
        private let maxHistoryLength: Int = 10//根据需求设置最多展示历史记录总数
         //获取之前的历史记录,搜索文本时调用
        lazy var historyArr: [String] = {
            if var temhistoryArr = UserDefaults.standard.object(forKey: "historyArrDefault") as? [String] {
                return temhistoryArr
            }
            return [String]()
        }()
        var searchHistoryBlock: ((String)->())?
        var updateHistoryV: (()->())? //刷新historyView
        func startSearch(text: String?){
            if let str = text,str.count > 0 {
                //判断是否有重复的,有则移除以前的
                if historyArr.contains(str) {
                    historyArr.removeAll{
                        $0 == str
                    }
                }
                //大于限制,移除最后一条
                if historyArr.count > maxHistoryLength-1 {
                    historyArr.removeLast()
                }
                //新增
                historyArr.insert(str, at: 0)
                updateHistoryV?()
                searchHistoryBlock?(str)
            }
        }
        
        //当前页面即将消失,存储历史搜索记录到缓存,可用NSUserDefault、归档、plist、splite等
        func saveHistoryArr() {
            UserDefaults.standard.set(historyArr, forKey: "historyArrDefault")
            UserDefaults.standard.synchronize()
        }
        
        //清空历史记录
        func clearhistory() {
            historyArr = [String]()
            saveHistoryArr()
            updateHistoryV?()
        }
        
    }
    

    demo地址:
    https://github.com/loveGithubs/history

    相关文章

      网友评论

        本文标题:Swift实现热门搜索和历史记录

        本文链接:https://www.haomeiwen.com/subject/xsqfkrtx.html