FavoriteWishViewController
MapViewController
SearchWishViewController
AddWishViewController
SelectWishViewController
등록된 노티피케이션에 노티피케이션 센터를 통해 정보를 전달하기 위한 구조체.
Notification을 발송하면 NotificationCenter에서 메세지를 전달한 옵저버를 처리할 때까지 대기합니다. 즉, 흐름이 동기적(synchronous)으로 흘러갑니다. 노티피케이션을 비동기적으로 사용하려면 NotificationQueue를 사용하면 된다.
class SelectWishViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// NotificationCenter에 옵저버 등록
NotificationCenter.default.addObserver(self, selector: #selector(convertToUIImageComplete(_ :)), name: NotificationName.ChangeImageNotification, object: nil)
}
extension WishViewModel {
func changeUIImage(index: Int){
ChangeUIImage.changeUIImage(imageURL: manager.wishs[index].imgURL){ [weak self] image in
self?.manager.wishs[index].img = image
// NoticationCenter에 발송
NotificationCenter.default.post(name: NotificationName.ChangeImageNotification, object: nil)
}
}
class SelectWishViewController: UIViewController {
@objc func convertToUIImageComplete(_ noti: Notification){
DispatchQueue.main.async {
self.indicator.stopAnimating()
self.presentAddWishListVC()
}
}
class SelectWishViewController: UIViewController {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
// NotificationCenter에서 옵저버 제거
NotificationCenter.default.removeObserver(self, name: NotificationName.ChangeImageNotification, object: nil)
}
https://zeddios.tistory.com/1052
https://velog.io/@sainkr/TIL-2021.02.02
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 5
configuration.filter = .any(of: [.images])
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
picker.modalPresentationStyle = .fullScreen
self.present(picker, animated: true, completion: nil)
extension AddImageViewController: PHPickerViewControllerDelegate {
@available(iOS 14, *)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
let loadUIImage = LoadUIImage(results.map(\.itemProvider))
loadUIImage.image{ images in
self.presentEditImageViewController(images)
}
}
}
class LoadUIImage{
private var itemProviders: [NSItemProvider] = []
init(_ itemProvdiers: [NSItemProvider]){
self.itemProviders = itemProvdiers
}
func image(completion: @escaping (([UIImage]) -> ())){
var iterator: IndexingIterator<[NSItemProvider]> = itemProviders.makeIterator()
var images: [UIImage] = []
while true {
guard let itemProvider = iterator.next(), itemProvider.canLoadObject(ofClass: UIImage.self) else { break }
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
if let error = error {
print("loadImageError : \(error)")
return
}
guard let image = image as? UIImage else { return }
images.append(image)
if images.count == self.itemProviders.count{
completion(images)
}
}
}
}
}
https://ntomios.tistory.com/15
https://velog.io/@sainkr/TIL-hb5ddnvb
class AddTagCollectionViewCell: UICollectionViewCell{
static let identifier = "AddTagCollectionViewCell"
private let titleLabel: UILabel = UILabel()
static func fittingSize(availableHeight: CGFloat, tag: String) -> CGSize {
let cell = AddTagCollectionViewCell()
cell.configure(tag: tag)
let targetSize = CGSize(
width: UIView.layoutFittingCompressedSize.width,
height: availableHeight)
return cell.contentView.systemLayoutSizeFitting(
targetSize,
withHorizontalFittingPriority: .fittingSizeLevel,
verticalFittingPriority: .required)
}
func configure(tag: String) {
titleLabel.text = tag
}
// ...
특정 뷰를 width, height의 우선순위에 따라 targetSize에 맞는 Size를 계산하여 반환해주는 함수
// MARK: - UICollectionViewDelegateFlowLayout
extension AddTagViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return AddTagCollectionViewCell.fittingSize(availableHeight: 45, tag: wishViewModel.tag(index: wishViewModel.lastWishIndex, tagIndex: indexPath.item))
}
}
https://velog.io/@sainkr/TIL-2021.03.06
let cropViewController = Mantis.cropViewController(image: 해당 이미지 (UIImage))
cropViewController.delegate = self
cropViewController.modalPresentationStyle = .fullScreen
self.present(cropViewController, animated: true, completion: nil)
extension EditImageViewController: CropViewControllerDelegate{
func cropViewControllerDidCrop(_ cropViewController: CropViewController, cropped: UIImage, transformation: Transformation) {
images[currentPage] = cropped
updatePageViewController()
dismiss(animated: true, completion: nil)
}
func cropViewControllerDidCancel(_ cropViewController: CropViewController, original: UIImage) {
dismiss(animated: true, completion: nil)
}
}
titleBackView.translatesAutoresizingMaskIntoConstraints = false
titleBackView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
titleBackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true
titleBackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
titleBackView.snp.makeConstraints{(make) in
make.top.equalToSuperview()
make.leading.equalToSuperview().inset(5)
make.bottom.equalToSuperview()
}
translatesAutoresizingMaskIntoConstraints = false를 해주지 않고 코드에서 제약을 걸어주면 translatesAutoresizingMaskIntoConstraints = true가 되어 AutorisizingMask를 통해 제약이 자동 생성 되면서 코드로 걸어준 제약과 충돌하기 때문에 translatesAutoresizingMaskIntoConstraints를 false로 설정해줘야 된다.
https://velog.io/@sainkr/TIL-2021.04.01
import Firebase
import FirebaseStorage
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Use Firebase library to configure APIs
FirebaseApp.configure()
let db = Database.database().reference().child("myWhisList")
let storage = Storage.storage()
db
.child(String(wish.timestamp))
.setValue([
"timestamp" : wish.timestamp,
"name": wish.name ,
"tag" : wish.tag,
"imgURL" : ["-"],
"memo" : wish.memo,
"link" : wish.link,
"placeName" : wish.place?.name ?? "-",
"placeLat": wish.place?.lat ?? 0,
"placeLng": wish.place?.lng ?? 0,
"favorite": wish.favorite])
self.db.observeSingleEvent(of:.value) { (snapshot) in
guard let wishValue = snapshot.value as? [String:Any] else { return }
// print("---> snapshot : \(wishValue.values)")
do {
// 1. DB에서 받아온 Foundation 값을 json 데이터로 만들어 준다.
let data = try JSONSerialization.data(withJSONObject: Array(wishValue.values), options: [])
let decoder = JSONDecoder()
// 2. [WishDB]로 디코딩 해준다.
let wish = try decoder.decode([WishDB].self, from: data)
}catch {
print("---> error : \(error)")
}
}
// compressionQuailty : 0.0 ~ 1.0 의 값을 가지며 JPEG 이미지의 품질을 나타냄. 0.0은 최대 압축 (lowest quailty) 1.0은 최소 압축 (best quailty)
let data = image.jpegData(compressionQuality: 0.1)!
let metaData = StorageMetadata()
metaData.contentType = "image/png" // contentType metaData 설정
let imageName = "\(wish.timestamp)\(i)"
storage.reference().child(imageName).putData(data, metadata: metaData) { (data, err) in
if let error = err {
print("--> error:\(error.localizedDescription)")
return
}
}
self.storage.reference().child(imageName).downloadURL { (url, err) in
if let error = err {
print("--> error:\(error.localizedDescription)")
return
}
guard let url = url else {
print("--> urlError")
return
}
imageURL.append(url.absoluteString)
if imageURL.count == wish.img.count {
completion(imageURL)
}
}
private func convertUIImagetoImageURL(wish: Wish, completion: @escaping (_ imageURL: [String]) -> Void){
var imageURL: [String] = []
for i in wish.img.indices{
let image: UIImage = wish.img[i]
// 지정된 이미지를 JPEG 형식으로 포함하는 데이터 개체를 반환합니다., JPEG 이미지의 품질 최대 압축 ~ 최소 압축
let data = image.jpegData(compressionQuality: 0.1)!
let metaData = StorageMetadata()
metaData.contentType = "image/png"
let imageName = "\(wish.timestamp)\(i)"
storage.reference().child(imageName).putData(data, metadata: metaData) { (data, err) in
if let error = err {
print("--> error:\(error.localizedDescription)")
return
}
self.storage.reference().child(imageName).downloadURL { (url, err) in
if let error = err {
print("--> error: \(error.localizedDescription)")
return
}
guard let url = url else {
print("--> urlError")
return
}
imageURL.append(url.absoluteString)
if imageURL.count == wish.img.count {
completion(imageURL)
}
}
}
}
}
캐시(cache)
주기억 장치에 읽어들인 명령이나 프로그램들로 채워지는 버퍼 형태의 고속 기억 장치. 중앙 처리 장치가 명령이 필요하게 되면, 맨 먼저 액세스하는 것은 주기억 장치가 아니라 캐시 메모리이다. 자주 액세스하는 데이터나 프로그램 명령을 반복해서 검색하지 않고도 즉각 사용할 수 있도록 저장해두는 영역이다.
[네이버 지식백과] 캐시 [cache] (컴퓨터인터넷IT용어대사전, 2011. 1. 20., 전산용어사전편찬위원회)
let url = URL(string:https://firebasestorage.googleapis.com/9a567ad81b57)
imageView.kf.setImage(with: url)
https://1consumption.github.io/posts/about-kingfisher(1)/ 를 참고하였습니다.