과제할 때 구글링해서 구현한 검색 기능과
수업시간에 배운 검색 기능을 따로 정리했다
navigationItem에 searchController를 추가한다
override func viewDidLoad() {
setupSearchController()
}
func setupSearchController() {
let searchController = UISearchController(searchResultsController: nil)
self.navigationItem.searchController = searchController
// text가 업데이트될 때마다 불리는 메소드
searchController.searchResultsUpdater = self
}
searchBar가 활성화되어 있고, searchBar에 텍스트가 쓰여있으면
기존 배열이 아닌 검색 필터가 된 배열이 테이블뷰에 나타나야 한다.
이를 판단하는 함수를 구현한다
var isFiltering: Bool {
let searchController = self.navigationItem.searchController
let isActive = searchController?.isActive ?? false
let isSearchBarHasText = searchController?.searchBar.text?.isEmpty == false
return isActive && isSearchBarHasText
}
2 - 1. isFiltering의 결과에 따라 다른 배열을 테이블뷰에 적용한다
extension SearchViewController: UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if self.isFiltering {
cell.textLabel?.text = self.filteredArr[indexPath.row]
}
else {
cell.textLabel?.text = self.arr[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.isFiltering {
return self.filteredArr.count
}
else {
return self.arr.count
}
}
}
searchBar에 입력된 텍스트를 기반으로 검색 필터가 된 배열을 정리한다
extension SearchViewController: UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
// 영어 검색할 수도 있으니까 lowercased
guard let text = searchController.searchBar.text?.lowercased() else {return}
self.filteredArr = self.arr.filter { $0.lowercased().contains(text) }
self.movieTableView.reloadData()
}
}
UISearchBar()
를 이용해서 searchBar를 생성한다 -> Delegate 채택(?)class LibraryCollectionViewController: UICollectionViewController {
let searchBar = UISearchBar()
override func viewDidLoad() {
super.viewDidLoad()
// 초기 화면에 모든 영화가 다 나오게 하기
searchList = list.movie
configureSearchBar()
collectionViewLayout()
}
func searchQuery(text: String) {
searchList.removeAll()
for item in list.movie {
if item.title.contains(text) {
searchList.append(item)
}
}
// 아무것도 입력하지 않았을 때는 모든 영화가 나오게 함
if text.count == 0 {
searchList = list.movie
}
collectionView.reloadData()
}
func configureSearchBar() {
searchBar.delegate = self
searchBar.placeholder = "검색어를 입력해주세요"
navigationItem.titleView = searchBar
}
}
extension LibraryCollectionViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let text = searchBar.text else { return }
searchQuery(text: text)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let text = searchBar.text else { return }
searchQuery(text: text)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = ""
// 캔슬 버튼을 눌렀을 때도 역시 모든 영화가 나오게 한다
searchList = list.movie
collectionView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = true
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = false
}
}
코드 상에서 바로 화면의 크기를 받아올 수 있다
print(UIScreen.main.bounds.width)
print(UIScreen.main.bounds.size)
print(UIScreen.main.bounds.height)
// 출력 (iPhone 14 Pro)
// 393.0
// (393.0, 852.0)
// 852.0
Collection View의 셀 크기를 지정할 때 유용하게 사용했다
func configureCollectionViewLayout() {
let layout = UICollectionViewFlowLayout()
let spacing: CGFloat = 15
let width = UIScreen.main.bounds.width - (spacing * 4)
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: width/3, height: width/3 + 20)
layout.sectionInset = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing)
layout.minimumLineSpacing = spacing
layout.minimumInteritemSpacing = spacing
maincollectionView.collectionViewLayout = layout
}
셀을 디자인하는 코드에서 배경 색을 랜덤으로 지정해주고 있었다.
좋아요 버튼을 누르면 reloadData()가 실행되기 때문에
collectionView의 모든 함수가 재실행되고
cellForItemAt도 재실행되고
그 안에 있던 designCell도 재실행되고
고 안에 있는 코드가 실행되면서 색을 다시 지정한다
// MovieCollectionViewCell.swift
class MovieCollectionViewCell: UICollectionViewCell {
@IBOutlet var backView: UIView!
func designCell(_ title: String, _ rate: String, _ like: Bool) {
// 배경 색 랜덤
// drand48() : [0.0, 1.0) 사이의 값을 반환
let rRed = CGFloat(drand48())
let rGreen = CGFloat(drand48())
let rBlue = CGFloat(drand48())
let setColor = UIColor(red: rRed, green: rGreen, blue: rBlue, alpha: 1.0)
backView.backgroundColor = setColor
}
}
셀의 정보를 저장하는 영화 struct에 배경 색을 추가한다
다른 요소와 마찬가지로 배경 색은 영화 struct의 인스턴스를 초기화할 때 지정한다
이 때, 랜덤으로 색을 반환하는 함수를 구조체의 타입 메서드로 선언한다
// MoviInfo.swift
struct Movie {
let title: String
let releaseDate: String
let runtime: Int
let overview: String
let rate: Double
var like: Bool
var backColor: UIColor
}
struct MovieInfo {
static func randomColor() -> UIColor {
let rRed = CGFloat(drand48())
let rGreen = CGFloat(drand48())
let rBlue = CGFloat(drand48())
let setColor = UIColor(red: rRed, green: rGreen, blue: rBlue, alpha: 1.0)
return setColor
}
var movie: [Movie] = [
Movie(title: "오예", releaseDate: "20", runtime: 1, overview: "다!", rate: 9.10, like: false, backColor : MovieInfo.randomColor(),
}
Cell 파일 내에 클로저를 저장할 수 있는 옵셔널 저장 프로퍼티를 추가한다
// TodoTableViewCell.swift
var specialCallBackMethod: (() -> Void)?
var doneCallBackMethod: (() -> Void)?
Cell 파일 내에 해당 버튼에 대한 @IBAction 함수를 생성하고,
버튼이 눌렸을 때 1에서 만든 클로저가 실행되도록 한다
// TodoTableViewCell.swift
@IBAction func specialButtonTapped(_ sender: UIButton) {
specialCallBackMethod?()
}
@IBAction func doneButtonTapped(_ sender: UIButton) {
doneCallBackMethod?()
}
Controller 파일 내에서 셀의 클로저를 만들어준다(?)
// TodoTableViewController.swift
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifier.TodoTableViewCell.rawValue) as! TodoTableViewCell
cell.doneCallBackMethod = { [weak self] in
if (indexPath.section == 0 && !((self?.list.specialList[indexPath.row].done)!) ) {
var tmp: ToDo = (self?.list.specialList[indexPath.row])!
self?.list.specialList.remove(at: indexPath.row)
tmp.done = true;
self?.list.specialList.insert(tmp, at: (self?.list.specialList.count)!)
}
else if (indexPath.section == 1 && !((self?.list.todoList[indexPath.row].done)!) ) {
var tmp: ToDo = (self?.list.todoList[indexPath.row])!
self?.list.todoList.remove(at: indexPath.row)
tmp.done = true;
self?.list.todoList.insert(tmp, at: (self?.list.todoList.count)!)
}
tableView.reloadData()
}
}
https://velog.io/@kevinkim2586/iOS-Swift-공부-Navigation-Item
https://developer.apple.com/documentation/uikit/uinavigationbar
https://developer.apple.com/documentation/uikit/uinavigationitem
https://developer.apple.com/documentation/uikit/uinavigationcontroller
navigationItem.title = "첫 번째 창"
navigationItem.titleView?.tintColor = .blue
navigationItem.titleView?.backgroundColor = .darkGray
navigationItem.title = "첫 번째 창"
navigationController?.navigationBar.backgroundColor = .systemPink
navigationController?.navigationBar.tintColor = .blue
navigationController?.navigationBar.barTintColor = .blue
3 - 1. 스크롤해도 기존 배경 색을 유지한다
navigationItem.title = "첫 번째 창"
navigationController?.navigationBar.backgroundColor = .systemPink
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
navigationController?.navigationBar.compactAppearance appearance
3 - 2. 글씨 색이 변한다. 하지만 스크롤 시 배경 색이 흐려진다.
navigationItem.title = "첫 번째 창"
navigationController?.navigationBar.backgroundColor = .systemPink
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
// let appearance = UINavigationBarAppearance()
// appearance.configureWithTransparentBackground()
//
// navigationController?.navigationBar.standardAppearance = appearance
// navigationController?.navigationBar.scrollEdgeAppearance = appearance
// navigationController?.navigationBar.compactAppearance = appearance
아예 타이틀이 나오지도 않는다
// button1
button1.titleLabel?.text = "첫 번째 버튼"
button1.titleLabel?.font = .systemFont(ofSize: 30)
button1.tintColor = .blue
타이틀은 나오지만, 폰트가 적용되지 않는다
여기서부터 슬슬 멘탈이 흔들렸다
// button 2
button2.setTitle("두 번째 버튼", for: .normal)
button2.titleLabel?.font = .systemFont(ofSize: 40)
button2.tintColor = .cyan
작은 버튼을 만드는게 목적이었기 때문에, 아예 버튼 사이즈를 mini로 설정했다
물론 글씨가 작아지긴 하지만, 여전히 폰트는 적용되지 않는다
// button 3
button3.setTitle("세 번째 버튼", for: .normal)
button3.configuration?.buttonSize = .mini
button2.titleLabel?.font = .systemFont(ofSize: 40)
수업시간에 배웠던 버튼의 configuration을 이용하는 방법을 시도했다
드디어 폰트가 적용되었다! 라고 생각했지만 버튼을 한 번 터치하면 다시 폰트가 초기화된다...
여기서 멘탈이 나갔다
// button 4
var config = UIButton.Configuration.plain()
config.title = "네 번째 버튼"
button4.configuration = config
button4.titleLabel?.font = .systemFont(ofSize: 40)
// button 5
button5.setTitle("다섯 번째 버튼", for: .normal)
button5.titleLabel?.font = .systemFont(ofSize: 40)
button5.tintColor = .gray
참고
https://babbab2.tistory.com/53
https://devsc.tistory.com/62
extension CALayer {
func addBorder(_ arr_edge: [UIRectEdge], color: UIColor, width: CGFloat) {
for edge in arr_edge {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect.init(x: 0, y: 0, width: frame.width, height: width)
break
case UIRectEdge.bottom:
border.frame = CGRect.init(x: 0, y: frame.height - width, width: frame.width, height: width)
break
case UIRectEdge.left:
border.frame = CGRect.init(x: 0, y: 0, width: width, height: frame.height)
break
case UIRectEdge.right:
border.frame = CGRect.init(x: frame.width - width, y: 0, width: width, height: frame.height)
break
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
}
class AddBorderViewController: UIViewController {
@IBOutlet var borderView: UIView!
@IBOutlet var borderLabel: UILabel!
@IBOutlet var borderTextField: UITextField!
@IBOutlet var borderButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
borderView.layer.addBorder([.top, .bottom], color: .blue, width: 2)
borderLabel.layer.addBorder([.left, .right], color: .systemPink, width: 2)
// textfield는 기본 border가 있기 때문에 제거해주고, addBorder를 실행시킨다
borderTextField.borderStyle = .none
borderTextField.layer.addBorder([.left, .bottom], color: .brown, width: 2)
borderButton.layer.addBorder([.top, .right], color: .cyan, width: 2)
navigationController?.navigationBar.layer.addBorder([.left, .bottom], color: .purple, width: 2)
tabBarController?.tabBar.layer.addBorder([.top, .right], color: .systemOrange, width: 2)
}
}