iOS SearchBar 사용해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


UISearchBar 사용해보기

서치 컨트롤러를 사용하다가 이래저래.. 서치바로 넘어왔습니다.

서치바는 아래와 같이 사용했습니다.

class SearchViewController: UIViewController {
    @IBOutlet weak var searchBar: UISearchBar!
    var isFiltering: Bool = false

    var arr = ["zehye", "hi", "hello", "nice", "to", "meet", "you"]
    var filterredArr: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.initUI()
        self.setSearchControllerUI()
    }

    func initUI() {
        self.tableView.delegate = self
        self.tableView.dataSource = self
    }

    func setSearchControllerUI() {
        self.searchBar.delegate = self
        self.searchBar.showsCancelButton = false
    }
}

extension SearchViewController: UITableViewDelegate, UITableViewDataSource {
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.isFilterting ? self.filterredArr.count : self.arr.count
    }

    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SeachListCell") as! SearchListTableViewCell
        if self.isFiltering {
            cell.textLabel?.text = self.filterredArr[indexPath.row]
        } else {
            cell.textLabel?.text = self.arr[indexPath.row]
        }

        return cell
    }
}

extension SearchViewController: UISearchBarDelegate {
    // 서치바에서 검색을 시작할 때 호출 
    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        self.isFiltering = true
        self.searchBar.showsCancelButton = true
        self.tableView.reloadData()
    }
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        guard let text = searchController.searchBar.text?.lowercased() else { return }
        self.filterredArr = self.arr.filter { $0.localizedCaseInsensitiveContains(text) }
       
        self.tableView.reloadData()
    }
    
    // 서치바에서 검색버튼을 눌렀을 때 호출 
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        dismissKeyboard()

        guard let text = searchController.searchBar.text?.lowercased() else { return }
        self.filterredArr = self.arr.filter { $0.localizedCaseInsensitiveContains(text) }
       
        self.tableView.reloadData()
    }
    
    // 서치바에서 취소 버튼을 눌렀을 때 호출 
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        self.searchBar.text = ""
        self.searchBar.resignFirstResponder()
        self.isFiltering = false
        self.tableView.reloadData()
    }
    
    // 서치바 검색이 끝났을 때 호출
    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
        self.tableView.reloadData()
    }
    
    // 서치바 키보드 내리기 
    func dismissKeyboard() {
        searchBar.resignFirstResponder()
    }
}

iOS CSV 파일을 파싱해서 테이블뷰에 뿌려보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


CSV 파일을 파싱해보기

csv파일을 파싱해 테이블뷰에 뿌려보려고 합니다.

우선 csv파일은 무엇일까요?
csv파일은 데이터들을 쉽표로 구분한 텍스트 데이터 혹은 텍스트 파일을 의미합니다.

확장자가 .csv인 것이죠.

예로들자면

우유,100,234
김밥,100,530

이런식으로 음식에 대한 데이터가 이름, 1회 제공량당 그램수, 1회 제공량당 칼로리 이렇게 쉽표로 구분되어져 보이는 텍스트 데이터를 의미합니다.

저는 이번에 식품의약품안전처에서 제공해주는 음식데이터들을 이름, 1회 제공량당 그램수, 1회 제공량당 칼로리로만 구분해놓은 csv파일을 파싱해 테이블뷰에 보여줄 예정인데요. 실제 식품의약품안전처에서는 이에 대한 정보를 api 혹은 엑셀파일을 제공해주고 있는데요. 해당 api를 통신을 통해 가져오려고 하니 너무 오랜 시간이 걸리더라구요.. 그래서 이번에는 엑셀파일을 csv로 변환하여 그 해당하는 파일을 프로젝트 내에서 파싱해 테이블뷰에서 보여줄 예정입니다.

우선 해당 엑셀파일을 csv로 변환해서(저장하는 방식을 .csv로 저장하면 쉽게 받을 수 있습니다) 받아두었고, 해당 csv파일을 엑스코드 프로젝트 내부에 옮겨놓았습니다.

이렇게 말이죠. 저는 Calorie라는 파일로 들어가져있습니다.

해당 파일에는 아래와 같이 데이터들이 보여집니다.

The 더 건강한 델리햄,100,230,
The 더 건강한 브런치 슬라이스 오리지널,100,120,
The 더 건강한 햄 치즈,100,330,

식품이름, 1회제공량당 그램수, 1회 제공량당 칼로리 이렇게 말이죠.

본격적으로 파싱해보기

실제로 저는 모델을 따로 만들어 두고있고, 해당 모델에서는 name, per, cal 이렇게 필드를 세개로 나누어 csv 파일 데이터들을 받아줄 예정입니다.

class SearchViewController: UIViewController {
    var foodList: [FoodModel] = []
}

이렇게 foodList라는 변수에 담아서 보여줄 예정입니다.

class SearchViewController: UIViewController {
    var foodList: [FoodModel] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        self.loadCalorieFromCSV()
    }

    private func parseCSVAt(url: URL) {
        do {
            // url을 받은 data
            let data = try Data(contentsOf: url)
            // 해당 data를 encoding 합니다.
            let dataEncoded = String(data: data, encoding: .utf8)
            // ,로 구분해 만든 리스트를 제가 만들어 놓은 foodList에 담아줍니다. (FoodModel이라는 model에 맞게)
            if let dataArr = dataEncoded?.components(separatedBy: "\n").map({$0.components(separatedBy: ",")}) {
                self.foodList = dataArr.compactMap({ FoodModel(value: $0) })
            }
        } catch {
            print("Error reading CSV file")
        }
    }
    
    private func loadCalorieFromCSV() {
        // bundle에 있는 경로 > Calorie라는 이름을 가진 csv 파일 경로 
        let path = Bundle.main.path(forResource: "Calorie", ofType: "csv")!
        parseCSVAt(url: URL(fileURLWithPath: path))

        self.tableView.reloadData()
    }
}

iOS CollectionView Cell Dynamic Size 지정해주기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


컬렉션뷰 셀의 사이즈를 동적으로 조절하고 싶다면?

위 사진같이 셀을 동적으로 사이징 처리 해주고 싶다면?

구글링해보면 너무 어렵게만 설명되어있다.
사실 별거 없다.

@IBOutlet weak var collectionView: UICollectionView!

func initUI() {
        self.collectionView.delegate = self
        self.collectionView.dataSource = self
        let layout = UICollectionViewFlowLayout()
        layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        self.collectionView.collectionViewLayout = layout
}

이렇게 해주면 셀 안의 데이터 크기에 맞게 알아서 사이징이 된다!

문제발견

그런데 문제를 하나 발견했다.
위에서 제시한 방법은 뷰컨에 바로 컬렉션뷰, 셀이 놓여져 있을떄만 적용이 되는 것 같다.

예로 들자면 컬렉션뷰 셀을 재사용하기 위해 셀을 nib로 따로 만들어놓고 이를 불러와서 사용해야한다면?

적용이 되지 않더라…..

그래서 내가 사용한 방법은 아래와 같다.
내 상황은 아래와 같다.

  1. 뷰컨에서 테이블뷰를 사용하고 있고 테이블 셀 안에 컬렉션 뷰가 있다.
  2. 컬렉션 뷰 셀은 재사용을 위해 따로 nib를 만들어놓았다.
  3. 따라서 테이블뷰 셀 파일에서 컬렉션뷰를 register하고 있다.

tableviewCell file

class DataTableViewCell: UITableViewCell {
  @IBOutlet weak var collectionView: UICollectionView!

  let list = ["밥", "된장찌개", "계란말이", "교촌허니콤보", "떡볶이"]
  let calList = ["12kCal", "365kCal", "1234kCal", "135kCal", "567kCal"]

  override func awakeFromNib() {
        super.awakeFromNib()
        initUI()
  }

  func initUI() {
    self.collectionView.register(UINib(nibName: "CalorieCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CalorieCell")
  }
}

extension DataTableViewCell: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let name = self.list[indexPath.row]
        let cal = self.calList[indexPath.row]

        let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14)]

        let nameSize = (name as NSString).size(withAttributes: attributes as [NSAttributedString.Key: Any])
        let calSize = (cal as NSString).size(withAttributes: attributes as [NSAttributedString.Key: Any])
        return CGSize(width: (nameSize.width + calSize.width) + 20, height: 35)
    }
}

이렇게 셀 안에 들어갈 string의 size를 측정해 직접 넣어주는 방법을 사용했다.

iOS controller 이동시 tabbar 사라지게 하는 방법

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Hide Bottom Bar on Push 를 체크해주면 된다.
push로 왔을 시 bottom영역을 hide 할것인지를 묻는 체크박스!

코드로 수정을 원한다면?

푸시로 화면이동을 하는경우

let vc = mainVC.instance()
vc.hideBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)

iOS Tableview Cell inset > Cell 사이의 간격 주는 방법

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Tableview Cell 사이의 간격 주는 방법

override func layoutSubviews() {
  super.layoutSubviews()
  contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))
}

위 코드에서 top, left, bottom, right를 내가 원하는 간격만큼 넣어주면 된다.