yeahg-dev / ios-project-manager Goto Github PK
View Code? Open in Web Editor NEWThis project forked from yagom-academy/ios-project-manager
할 일을 저장하고 관리하는 아이패드 앱
This project forked from yagom-academy/ios-project-manager
할 일을 저장하고 관리하는 아이패드 앱
UITableView
에 sectionHeader와 Cell 사이의 seperator를 구현하기 위해서
ProjectHeaderView의 bottom에만 border를 그려주도록 구현하고자함
ProjectHeaderView에 seperator가 될 sublayer를 추가
override func layoutSubviews() {
super.layoutSubviews()
let seperator = CALayer()
seperator.frame = CGRect(x: 0, y: self.bounds.height - 0.5, width: self.bounds.width, height: 1)
seperator.backgroundColor = UIColor.gray.cgColor
self.layer.addSublayer(seperator)
}
처음엔 override init(frame: CGRect)
에서 위 코드를 구현했지만 적용이 되지 않음.
init
이 호출되는 시점엔 self의 레이아웃이 결정되기 전이기 때문에 bounds가 없어서 적용이 되지 않는 다는 것을 알게됨.
따라서 layoutSubviews
를 override해서 self의 bounds가 결정된 이후, sublayer를 설정하고 적용하도록 수정함.
ProjectLocalDataBase
구현DataSource
프로토콜 구현ProjectFirestoreBase
구현제약간 충돌 때문에 마진으로 준 제약(10)이 적용이 안된다고 판단했지만,
제약 충돌을 해결하고도 10이 반영이 되지 않는 것을 확인
뷰 디버거에서 보여지는 Popover의 사이즈보다 실제 화면에서 보여지는 뷰의 사이즈가 더 작은 것을 확인
self.view
의 넓이와 높이를 정해주는 제약을 없애고, self.preferredContentSize = CGSize(width: 300, height: 150)
로 넓이 높이를 정의
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.preferredContentSize = CGSize(width: 300, height: 150)
}
// MARK: - Method
private func configureViewHeirachry() {
self.view.addSubview(contentStackView)
}
private func configureLayout() {
NSLayoutConstraint.activate([
contentStackView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 30),
contentStackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
contentStackView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -30)
])
}
스택뷰와 뷰간의 제약을 더 크게(30)을 줌으로서 마진이 적용되도록 수정
ProjectBoardViewController
구현ProjectDetailViewController
구현+
버튼 터치시 프로젝트 추가 화면
표시 구현ProjectPreviewTableViewCell
구현
ProjectTableViewHeaderView
구현
projectCountLabel
뱃지 형태로 구현projectTableView
의 섹션과 셀 사이의 seperator 구현TodoTableViewController
구현, DiffableDataSource 구현DoingTableViewController
구현, DiffableDataSource 구현DoneTableViewController
구현, DiffableDataSource 구현진행 상태 옮기기 팝오버
구현프로젝트 상세 화면 표시
구현ProjectTableViewCell
의 말줄임표 기능DataSourceConfigViewController
뷰 제약 충돌UITableViewDiffableDataSource
로 테이블 뷰의 dataSource를 구현했습니다.
tableView의 Header를 커스텀하게 만들기 위해 UITableViewHeaderFooterView
를 상속한 클래스를 만들어서,
tableViewDelegate에서 지정해주었습니다.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = projectTableView.dequeueReusableHeaderFooterView(
withClass: ProjectTableViewHeaderView.self)
let snapshot = dataSource.snapshot()
let projectCount = snapshot.numberOfItems(inSection: .main)
header.configureContent(status: String(describing: projectStatus),
projectCount: String(projectCount))
return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
tableView의 Cell에 CRUD가 일어날 때 마다 프로젝트 갯수를 카운팅하는 projectCountLabel
을 업데이트 해줘야 하는데요,
그러기 위해선 SectionHeader를 업데이트 해야합니다.
UITableViewDiffableDataSource
레벨에서 처리해주기 위해 API를 찾아봤지만,
UITableViewDiffableDataSource
에서는 UICollectionViewDiffableDataSource
와는 달리 SupplementaryView를 설정해주는 API가 제공되지 않고 있었습니다.
UITableView
의 reloadData( )
로 Header를 업데이트 할 수 있었지만, reloadData( )
모든 Section과 Row를 그리는 건 무거운 작업이고,
diff만 계산해서 효율적으로 뷰를 그려주는 UIDiffableDataSource
의 이점을 살리지 못하는 방법이라고 생각했습니다.
UITableViewHeaderFooterView
를 리턴하는 방법 포기updateProjectCells()
를 업데이트 해주었습니다.ProjectHistoryViewController
의 view사이즈를 historyTableView
의 사이즈와 동일하게 만들어,
tableview와 핏한 뷰를 만들고자 함
viewDidLayoutSubview
구글링을 한 결과 viewDidLayoutSubview
에서 아래와 같은 코드를 구현하면 가능하다고 함.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.preferredContentSize = self.historyTableView.contentSize
}
하지만 실행결과 tableView의 실제 높이를 반영하지 못하고 오류를 발생시킴.
preferredContentSize가 계속해서 layout을 조정하고, 따라서 재귀적으로 viewDidLayoutSubview
를 호출시킴
.systemLayoutSizeFitting
https://useyourloaf.com/blog/self-sizing-popovers/
override func viewDidLoad() {
super.viewDidLoad()
self.configureViewLayout()
self.configureHistoryTableView()
self.preferredContentSize = self.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
❌ contstraints 충돌 발생
테이블뷰의 셀이 아직 deque되지 않은 시점이기 때문에 테이블뷰의 높이를 반영하지 못함
Cell contentView frame의 inset을 부여해서 Cell간의 spacing을 구현했습니다.
override func layoutSubviews() {
super.layoutSubviews()
self.contentView.frame = contentView.frame.inset(by: UIEdgeInsets(
top: 5,
left: 0,
bottom: 5,
right: 0)
)
}
그러나 기존에는 정상적으로 표시되던 StackView안의 Label들이 씹히고, Label간의 spacing이 일정하지 않은 문제가 발생했습니다.
spacing효과를 주기 위해서 Cell ContentView안에 깡통뷰를 만들고, 깡통뷰와 contentView 사이에 제약을 설정했습니다.
private func configureLayout() {
NSLayoutConstraint.activate([
// cellContainerView ◀️ 깡통뷰
cellContainerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
cellContainerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
cellContainerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
cellContainerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5)
])
let margin = CGFloat(15)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: cellContainerView.topAnchor, constant: margin),
stackView.bottomAnchor.constraint(equalTo: cellContainerView.bottomAnchor, constant: -margin),
stackView.leadingAnchor.constraint(equalTo: cellContainerView.leadingAnchor, constant: margin),
stackView.trailingAnchor.constraint(equalTo: cellContainerView.trailingAnchor, constant: -margin)
])
}
LightMode | DarkMode |
---|---|
![]() |
![]() |
![]() |
![]() |
.label
.systemBackground
, .systemRed
와 같은 systemColor를 이용해서 자동으로 대응하도록 구현
private var titleTextField: UITextField = {
let textField = UITextField()
...
textField.backgroundColor = .systemBackground
textField.textColor = .label
textField.layer.shadowColor = UIColor.shadowColor.cgColor
...
return textField
}()
ProjectTableViewCell의 backgroundcolor의 다크모드 색상은 커스텀하게 설정하기 위해 colorset 생성
self.cellContainerView.backgroundColor = UIColor(named: "projectCellColor")
layer.shadowColor
: func traitCollectionDidChange(_:)
// shadowColor정의
extension UIColor {
static var shadowColor: UIColor {
if #available(iOS 13, *) {
return UIColor { (traitCollection: UITraitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .light {
return .systemGray
} else {
return .white
}
}
} else {
return .�systemGray
}
}
}
CGColor는 모드가 변경될때마다 각 모드에 대응하는 컬러를 자동으로 리턴하지 못함으로
func traitCollectionDidChange(_:)
내에서 업데이트시킴
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
guard #available(iOS 13, *) else { return }
guard traitCollection.userInterfaceStyle != previousTraitCollection?.userInterfaceStyle else { return }
titleTextField.layer.shadowColor = UIColor.shadowColor.cgColor
descriptionTextView.layer.shadowColor = UIColor.shadowColor.cgColor
}
알림 설정
UI 추가 private var descriptionTextView: UITextView = {
let textView = UITextView(frame: .zero)
textView.translatesAutoresizingMaskIntoConstraints = false
textView.backgroundColor = .systemGray6
textView.font = .preferredFont(forTextStyle: .body)
textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
textView.layer.shadowOffset = CGSize(width: 3, height: 3)
textView.layer.shadowOpacity = 0.3
textView.layer.shadowRadius = 3
textView.layer.masksToBounds = false // 그림자는 bound밖에 생기므로, true이면 그림자가 보이지 않음
return textView
}()
UITextView
에 그림자를 넣기 위해 layer.masksToBound = false
를 하면
bound영역 밖의 text들도 보여지는 문제가 발생합니다.
TextView를 담는 Container View를 만들고, Container View에 그림자효과를 줬습니다.
private var descriptionTextViewContainer: UIView = {
let view = UIView(frame: .zero)
view.layer.shadowOffset = CGSize(width: 3, height: 3)
view.layer.shadowOpacity = 0.3
view.layer.shadowRadius = 3
view.layer.masksToBounds = false
return view
}()
private var descriptionTextView: UITextView = {
let textView = UITextView(frame: .zero)
textView.translatesAutoresizingMaskIntoConstraints = false
textView.backgroundColor = .systemGray6
textView.font = .preferredFont(forTextStyle: .body)
textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
textView.layer.masksToBounds = true
return textView
}()
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.