Skip to content

πŸ“ λ‚˜μ— λŒ€ν•΄ μ•Œμ•„κ°€λŠ” 질문 닀이어리, BeMe

Notifications You must be signed in to change notification settings

TeamBeMe/BeMeiOS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BeMeiOS

πŸ“Œ ν”„λ‘œμ νŠΈ μ†Œκ°œ

  • SOPT 27th μž₯κΈ° 해컀톀 APPJAM
  • κΈ°κ°„: 2020.12.26 ~ 2021.1.16

맀일 μ§ˆλ¬Έμ— 닡을 ν•˜λ©° λ‚˜λ₯Ό μ•Œμ•„κ°€λŠ” μ§ˆλ¬Έλ‹€μ΄μ–΄λ¦¬ BeMe μž…λ‹ˆλ‹€.

24μ‹œκ°„λ§ˆλ‹€ μƒˆλ‘œμš΄ 질문 제곡, μƒˆλ‘œμš΄ 질문 λ°›κΈ°λ₯Ό 톡해 μ§ˆλ¬Έμ— 닡을 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μžμ‹ μ˜ 글을 전체 κ³΅κ°œν•˜μ—¬ μ‚¬λžŒλ“€κ³Ό μ†Œν†΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ λ§ˆμ΄νŽ˜μ΄μ§€μ—μ„œ λ‚΄κ°€ μ§€κΈˆκΉŒμ§€ λ‹΅ν•œ μ§ˆλ¬Έμ„ μ‰½κ²Œ λ³Ό 수 있으며, λ‚΄ 생각이 μ–΄λ–»κ²Œ λ³€ν–ˆλŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“Œ 개발 ν™˜κ²½ 및 라이브러리

개발 ν™˜κ²½

Swift 4 Xcode swift iOS COCOAPODS

라이브러리

라이브러리(Library) λͺ©μ (Purpose) 버전(VersionA
Alamofire μ„œλ²„ 톡신 Alamofire
Kingfisher 이미지 처리 Kingfisher
SnapKit μ˜€ν† λ ˆμ΄μ•„μ›ƒ Kingfisher
Then 짧은 μ½”λ“œ 처리 Kingfisher
lottie-ios λ‘œν‹° μ• λ‹ˆλ©”μ΄μ…˜ 처리 lottie-ios
Firebase/Messaging Firebase Cloud Messaging Kingfisher
SwiftLint κΉ”λ”ν•œ μ½”λ”© μ»¨λ²€μ…˜ Kingfisher

AutoLayout 적용 μ—¬λΆ€

1. iPhone 12 Pro 적용

2. iPhone 12 mini 적용

3. iPhone SE2 적용

πŸ“Œ μ„œλΉ„μŠ€ workflow

πŸ“Œ ν˜‘μ—… 방식

πŸ“Œ κΈ°λŠ₯별 개발 μ—¬λΆ€

ν™”λ©΄ κΈ°λŠ₯ 상세 κΈ°λŠ₯ 개발
μŠ€ν”Œλž˜μ‹œ μœ€μž¬πŸš€
μ˜¨λ³΄λ”© μœ€μž¬πŸš€
νšŒμ›κ°€μž… μœ€μž¬πŸš€
둜그인 둜그인 μœ€μž¬πŸš€
μžλ™λ‘œκ·ΈμΈ μœ€μž¬πŸš€
ν™ˆ 였늘의 질문 및 λ‹΅λ³€ μ—΄λžŒ μœ€μž¬πŸš€
과거의 질문 및 λ‹΅λ³€ μ—΄λžŒ μœ€μž¬πŸš€
μΆ”κ°€ 질문 μ—΄λžŒ μœ€μž¬πŸš€
λ‹΅λ³€ 곡개 λ²”μœ„ μ„€μ • μœ€μž¬πŸš€
λ‹΅λ³€ μˆ˜μ • 및 μ‚­μ œ μœ€μž¬πŸš€
탐색 λ‚˜μ™€ λ‹€λ₯Έ λ‹΅λ³€λ“€ μ—΄λžŒ 재용🐢
λ‹€λ₯Έ λ‹΅λ³€λ“€ λ‘˜λŸ¬λ³΄κΈ° μ΅œμ‹ , ν₯λ―Έ νƒ­ 및 ν‚€μ›Œλ“œ ν•„ν„° 검색 재용🐢
슀크랩/ μ–ΈμŠ€ν¬λž© 재용🐢
μ‹ κ³ ν•˜κΈ° 재용🐢
νŒ”λ‘œμž‰ νŒ”λ‘œμž‰/ νŒ”λ‘œμ›Œμ˜ λ‹΅λ³€ μ—΄λžŒ μœ€μž¬πŸš€
νŒ”λ‘œμž‰/ νŒ”λ‘œμ›Œ μ—΄λžŒ μœ€μž¬πŸš€
νŒ”λ‘œμž‰/ νŒ”λ‘œμ›Œ 검색 μ‚¬μš©μž 검색 μœ€μž¬πŸš€
νŒ”λ‘œμš°/ μ–ΈνŒ”λ‘œμš° μœ€μž¬πŸš€
νŒ”λ‘œμ›Œ μ‚­μ œ μœ€μž¬πŸš€
μ‹ κ³ ν•˜κΈ° μœ€μž¬πŸš€
λ§ˆμ΄νŽ˜μ΄μ§€ ν”„λ‘œν•„ νŽΈμ§‘ μœ€μž¬πŸš€
λ‚΄ κΈ€, 슀크랩 μ—΄λžŒ μ„Έλž€πŸŒΏ
ν‚€μ›Œλ“œ 및 ν•„ν„° 검색 μ„Έλž€πŸŒΏ
λ‹΅λ³€ 곡개 λ²”μœ„ μ„€μ • μ„Έλž€πŸŒΏ
슀크랩/ μ–ΈμŠ€ν¬λž© μ„Έλž€πŸŒΏ
κΈ€ 상세 λ‹΅λ³€ λ‹΅λ³€ 상세 μ—΄λžŒ 재용🐢
λ‹΅λ³€ μˆ˜μ • 및 μ‚­μ œ 재용🐢
슀크랩/μ–ΈμŠ€ν¬λž© 재용🐢
μ‹ κ³ ν•˜κΈ° 재용🐢
λŒ“κΈ€ λŒ“κΈ€ μž‘μ„± 재용🐢
λŒ“κΈ€μ˜ λ‹΅κΈ€ μž‘μ„± 재용🐢
λŒ“κΈ€ κ³΅κ°œλ²”μœ„ μ„€μ • 재용🐢
λŒ“κΈ€ μˆ˜μ • 및 μ‚­μ œ 재용🐢
κΈ€ μ“°κΈ° κ³΅κ°œλ²”μœ„ 및 λŒ“κΈ€ κΈ°λŠ₯ μ„€μ • μœ€μž¬πŸš€, μ„Έλž€πŸŒΏ
μž„μ‹œμ €μž₯ μ„Έλž€πŸŒΏ
λ‹΅λ³€ν•˜κΈ° μ„Έλž€πŸŒΏ
νƒ€μΈνŽ˜μ΄μ§€ νŒ”λ‘œμš°/ μ–ΈνŒ”λ‘œμš° μ„Έλž€πŸŒΏ
슀크랩/ μ–ΈμŠ€ν¬λž© μ„Έλž€πŸŒΏ
μ‹ κ³ ν•˜κΈ° μ„Έλž€πŸŒΏ
졜근 ν™œλ™ λŒ“κΈ€ 및 λ‹΅κΈ€ μ•Œλ¦Ό
νŒ”λ‘œμ›Œ μ•Œλ¦Ό
μž₯κΈ° 푸쉬 μ•Œλ¦Ό 였늘의 질문 λ°›κΈ° μœ€μž¬πŸš€

πŸ“Œ 핡심 κΈ°λŠ₯ κ΅¬ν˜„ 방법

  • κΈ€μ“°κΈ°

answer dataλ₯Ό λ„£μ–΄μ„œ 글을 λ“±λ‘ν•˜λŠ” μ½”λ“œ

AnswerRegistService.shared.regist(answerID: answerData!.id!, content: answerData!.answer!, commentBlocked: commentSwitch.isOn, isPublic: answerSwitch.isOn) {(networkResult) -> (Void) in
    switch networkResult{
    case .success(let data) :
        print(self.isFromFollowingTab)
        if self.isFromFollowingTab {
            self.followScrapButtonDelegate?.fromAnswerVC(indexInVC: self.indexInFollowingVC!)
        }
    case .requestErr(let msg):
        if let message = msg as? String {
            print(message)
        }
    case .pathErr :
        print("pathErr")
    case .serverErr :
        print("serverErr")
    case .networkFail:
        print("networkFail")
        
    }
}
  • 타인이 μ“΄ κΈ€ 보기

λ‹€λ₯Έ νŽ˜μ΄μ§€λ₯Ό λ‹€λ…€μ™€μ„œ λ‹€λ₯Έ μ‚¬λžŒμ˜ 글에 변동이 생길 경우 μƒˆλ‘œ 톡신을 ν•΄μ„œ Reloadν•΄μ€˜μ•Όν•¨ νŽ˜μ΄μ§• ν•΄μ„œ 정보λ₯Ό λ°›κΈ° λ•Œλ¬Έμ— 1νŽ˜μ΄μ§€λΆ€ν„° μ›λž˜μ˜ νŽ˜μ΄μ§€κΉŒμ§€ λ‹€μ‹œ 받아와야 함 이 κ³Όμ •μ—μ„œ μ²˜μŒμ—λŠ” for문을 μ΄μš©ν•΄μ„œ 어렀움을 κ²ͺμ—ˆμœΌλ‚˜, ν†΅μ‹ μ—μ„œμ˜ μž¬κ·€ν•¨μˆ˜ ν˜ΈμΆœμ„ 톡해 ν•΄κ²°

func getUpdateAnswers(){
    let loadingFrame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
    LoadingHUD.show(loadingFrame: loadingFrame,color: .white)
    curPage = 0
    answers = []
    updateDataOnePage()
}

func updateDataOnePage(){
    
    curPage += 1
    let loadingFrame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
    FollowingGetAnswersService.shared.getAnswerData(page: curPage){(networkResult) -> (Void) in
        switch networkResult{
        case .success(let data) :
            if let answerDatas = data as? FollowingAnswersData {
                self.pageLen = answerDatas.pageLen
                for answerData in answerDatas.answers{
                    self.answers.append(answerData)
                }
                
            }
            if self.curPage < self.answerPage {
                self.updateDataOnePage()
            }
            else{
                DispatchQueue.global(qos: .background).sync {
                    self.wholeCollectionView.reloadData()
                    self.wholeCollectionView.setContentOffset(CGPoint(x: 0, y: self.lastY), animated: false)
                }
                
                LoadingHUD.hide()
                if self.answers.count == 0{
                    self.customEmptyView.setItems(text: "아이쿠..! 아직 νŒ”λ‘œμš°ν•˜λŠ” μ‚¬λžŒμ΄ μ—†κ΅°μš”\nνŒ”λ‘œμž‰μ„ ν•˜κ³  λ‹€λ₯Έ μ‚¬λžŒλ“€μ˜ 글을 λ‘˜λŸ¬λ³΄μ„Έμš”")
                    self.wholeCollectionView.addSubview(self.customEmptyView)
                    print(self.customEmptyView.superview?.bounds.minX)
                    self.customEmptyView.snp.makeConstraints{
                        $0.centerX.equalToSuperview()
                        $0.top.equalToSuperview().offset(362)
                        $0.height.equalTo(80)
                    }
                   
    
                }
                else{
                    self.customEmptyView.removeFromSuperview()
                }
    
              
            }

            print("success")

        case .requestErr(let msg):
            if let message = msg as? String {
                print(message)
            }
        case .pathErr :
            print("pathErr")
        case .serverErr :
            print("serverErr")
        case .networkFail:
            print("networkFail")
            
        }
        
        
    }
    
    
}

πŸ“Œ Extension을 ν†΅ν•œ λ©”μ†Œλ“œ μ„€λͺ…

  • UIView Extension

    extension UIView {
        // Set Rounded View
        func makeRounded(cornerRadius : CGFloat?){
            
            // UIView 의 λͺ¨μ„œλ¦¬κ°€ λ‘₯κ·Ό 정도λ₯Ό μ„€μ •
            if let cornerRadius_ = cornerRadius {
                self.layer.cornerRadius = cornerRadius_
            }  else {
                // cornerRadius κ°€ nil 일 경우의 default
                self.layer.cornerRadius = self.layer.frame.height / 2
            }
            
            self.layer.masksToBounds = true
        }
        
        // Set UIView's Shadow
        func dropShadow(color: UIColor, offSet: CGSize, opacity: Float, radius: CGFloat) {
            
            // 그림자 색상 μ„€μ •
            layer.shadowColor = color.cgColor
            // 그림자 크기 μ„€μ •
            layer.shadowOffset = offSet
            // 그림자 투λͺ…도 μ„€μ •
            layer.shadowOpacity = opacity
            // 그림자의 blur μ„€μ •
            layer.shadowRadius = radius
            // ꡬ글링 ν•΄λ³΄μ„Έμš”!
            layer.masksToBounds = false
            
        }
        
        // Set UIView's Border
        func setBorder(borderColor : UIColor?, borderWidth : CGFloat?) {
            
            // UIView 의 ν…Œλ‘λ¦¬ 색상 μ„€μ •
            if let borderColor_ = borderColor {
                self.layer.borderColor = borderColor_.cgColor
            } else {
                // borderColor λ³€μˆ˜κ°€ nil 일 경우의 default
                self.layer.borderColor = UIColor(red: 205/255, green: 209/255, blue: 208/255, alpha: 1.0).cgColor
            }
            
            // UIView 의 ν…Œλ‘λ¦¬ λ‘κ»˜ μ„€μ •
            if let borderWidth_ = borderWidth {
                self.layer.borderWidth = borderWidth_
            } else {
                // borderWidth λ³€μˆ˜κ°€ nil 일 경우의 default
                self.layer.borderWidth = 1.0
            }
        }
    }
  • UIViewController

    extension UIViewController {
        // ν† μŠ€νŠΈ 메세지
        func showToast(text: String, completion: @escaping ()->()) {
            let toast = ToastView(frame: CGRect(x: 0, y: 0, width: 343, height: 84))
            toast.setLabel(text: text)
            toast.alpha = 0
            self.view.addSubview(toast)
            toast.snp.makeConstraints {
                $0.leading.equalToSuperview().offset(16)
                $0.trailing.equalToSuperview().offset(-16)
                $0.bottom.equalToSuperview().offset(-101)
            }
            UIView.animate(withDuration: 0.3, animations: {
                toast.alpha = 1
                
            },completion: { finish in
                UIView.animate(withDuration: 0.3, delay: 0.7, animations: {
                    toast.alpha = 0
    
                }, completion: { finish in
                    if finish {
                        toast.removeFromSuperview()
                        completion()
                    }
                })
            })
        }
    }
  • UIImageView

    // Kingfisherλ₯Ό μ΄μš©ν•˜μ—¬ urlλ‘œλΆ€ν„° 이미지λ₯Ό κ°€μ Έμ˜€λŠ” extension
    extension UIImageView {
        
        public func imageFromUrl(_ urlString: String?) {
            if let url = urlString {
                self.kf.setImage(with: URL(string: url), options: [.transition(ImageTransition.fade(0.5))])
            }
        }
    }

πŸ“Œ νŒ€μ› μ†Œκ°œ

윀재 μ„Έλž€ 재용
λ„€μ΄μŠ€

λͺ©μ°¨

About

πŸ“ λ‚˜μ— λŒ€ν•΄ μ•Œμ•„κ°€λŠ” 질문 닀이어리, BeMe

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published