iOS SwiftLint 적용해보기

|

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


SwiftLint 적용해보기

이전에 글에서는 SwiftLint를 적용했을 때 발생하는 경고들에 대한 해결방안에 대해서만 정리를 했었습니다. > iOS SwiftLint 적용하며 수정한 것들

이번글에서는 좀 더 근간이 되는.. SwiftLint 자체를 프로젝트에 어떻게 적용하는지? 에 대한 방법을 정리해보도록 하겠습니다.

우선 프로젝트에 vi Podfile 한 후 SwiftLint를 추가해줍니다. > pod install

pod 'SwiftLint'

그리고 프로젝트로 돌아와 Target > build phase > + > new run script phase 절차를 밟아줍니다.
그리고 아래 사진처럼 run script에 해당 코드를 추가해 주세요.

${PODS_ROOT}/SwiftLint/swiftlint

그리고 빌드를 해줍니다…
그러면 에러든.. 경고든… 뭔가가…우수수… 나타나게 될 것입니다..

이 swiftLint에는 정말 다양하고도 많고 많은 규칙들이 나오게 되는데요.
이 모든것들을 제가 다 컨트롤 하지못하고 그저 시키는대로만 해야한다면.. 그건 또 너무 힘들겠죠?

그래서 사실 이 규칙들을 또 제가 컨트롤 할수도 있습니다.

하는 방법은 아래와 같습니다.

새로운 파일을 만듭니다. Empty 파일로 만들어주세요.
여기서 중요한 뽀인트는 프로젝트에 파일을 추가해야 하는 것입니다. 프로젝트 > 앱 이름 폴더 안이 아니구요!
프로젝트 자체에 추가!!!!!!!!!!!!!
그리고 이름은 무조건 .swiftlint.yml 로 지어주세요. 무 족 권

그러면 경고창으로 파일 이름 앞에 .을 붙이면 숨김 파일 되는데 너 쓸거야? 라고 물어봅니다. 응~ 쓸거야(Use “.”) 버튼 눌러줍니다.

그리고 이곳에서 각자 예외를 만들어줄 것들을 추가해줍니다.

# 파일 length
file_length: 1200

# 깊이
nesting:
  type_level: 3

# 한줄 길이
line_length: 300

# 함수 길이
function_body_length: 160

# body 길이
type_body_length: 500

# 함수 복잡성
cyclomatic_complexity: 60

# 변수명
identifier_name:
  min_length: 2

# 튜플 최소갯수
large_tuple: 3

# 타입길이
type_name:
  min_length: 2
  max_length:
    warning: 100
    error: 150

# 규칙제거 - 축약
disabled_rules:
- syntactic_sugar
- trailing_whitespace
- compiler_protocol_init

# 제외 경로
excluded:
  - Pods

저는 대략적으로 이렇게 짜놓았습니다.

여기서 집고 넘어갈 부분은 disabled_rules, excluded 정도가 있겠네요.

1. disabled_rules

swiftLint에서 디폴트로 적용되는 규칙들을 내 프로젝트에서는 아닌 상태로 만들어줍니다.

2. excluded

swiftLint가 검사하지 않을 영역을 지정해주는 것입니다.
특정 파일에서 경고가 뜨는데, 별로 보고싶지 않다면 이 특정 폴더 혹은 파일을 제외시킬 수도 있게 됩니다.

저 같은 경우는 코코아팟 라이브러리들에서 나는 에러와 경고는 모두 무시할 수 있도록 지정해놓았습니다.

3. included

추가로 included같은 경우는 유추 가능하듯 반드시 검사가 필요한 영역에 사용해볼 수 있겠습니다.
근데 저는 이 included는 사용하지 않을 것 같네욤..ㅎ..

iOS UIPageControl 사용해보기

|

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


UIPageControl 사용해보기

이번 프로젝트에서 저는 UIPageControl을 UICollecionView와 함께 사용한 경험이 있습니다.
정확히는 테이블뷰 셀 위에 컬렉션뷰를 놓았고, 그 컬렉션뷰 셀에 대한 페이지 컨트롤을 동작하게 만들어 놓았습니다.

다음 사용방법은 아래와 같습니다.

class HomeTableViewCell: UITableViewCell{
    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var pageControl: UIPageControl!

    override func awakeFromNib() {
        super.awakeFromNib()

        // pageControl 
        pageControl.numberOfPages = 2
        pageControl.currentPage = 0
        pageControl.pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.3)
        pageControl.currentPageIndicatorTintColor = UIColor(named: "7A7BDA")
        pageControl.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
    }
}

extension HomeTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        // 현재까지 스크롤된 크기를 구함
        let page = Int(targetContentOffset.pointee.x / self.frame.width)
        self.pageControl.currentPage = page
      }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let width = scrollView.bounds.size.width
        let x = scrollView.contentOffset.x + (width/2)
        
        let newPage = Int(x/width)
        if pageControl.currentPage != newPage {
            pageControl.currentPage = newPage
        }
    }
}

이때 여기서 가장 중요한 포인트는 코드에 있지않습니다.

일반적으로 생각해보면 페이지컨트롤이 컬렉션뷰 안에 존재할것같다고 생각할수도 있습니다.
그렇지만 그렇지 않습니다. 컬렉션뷰 바깥에 같은 동일선상에 존재하고 있어야 합니다.

이렇게 말이죠!

iOS AppIcon 추가해보기

|

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


AppIcon 추가해보기

  1. 1024px 이상의
  2. 사진은 정방형이여야 하고, 확장자는 png 이면서
  3. 제일 중요하게 불투명한 배경을 가진 이미지인 앱 아이콘 이미지를 준비한다.

App Icon Generator

그리고 이 홈페이지에 접속을 하면 준비된 이미지를 드래그 해놓기만 하면 이미지들이 형성이 됩니다.

이렇게 다운로드 파일에 들어가 AppIcon 파일이 저장되어있는 것을 확인했다면 AppIcon.appiconset 안에 생성된 이미들이 종류별로 있음을 확인 가능할 것입니다. 이때 이 AppIcon.appiconset 파일을 통째로 프로젝트 내 assets에 임포트(import) 해줍니다.

그리고 Xcode를 재실행(빌드) 해보고 나면 원했던 앱 아이콘이 제대로 적용 되어있음을 볼수있을 것 입니다.

만약 반영이 안된다면, Target > Build settings > Asset Catalog compiler - Options - Primary App Icon Set NameAppIcon 으로 되어있는지 확인을 해봅시다! 혹은 Assets에 있는 기본 AppIcon 파일을 지우고 직접 만들어 이미지를 임포트 해주면 추가가 되기도 합니다!

Swift 나누기 할 때 소수점 이하 숫자가 안나오는 경우(0.0으로만 나오는 경우)

|

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


나누기 할 때 소수점 이하 숫자가 안나오는 경우

프로젝트를 진행하고 있는데, 나누기를 할때 소수점 이하의 숫자가 나오질 않는다.
고로, 만약 2/5를 한다고 하면 0.4 이렇게 나오는것이 아닌 그냥 0.0으로 찍힌다.

해결방법은 간단하다.

print(Double(2)/Double(5))

iOS Custom Calendar 직접 만들어보기

|

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


Custom Calendar 직접 만들어보기

일반적으로 우리가 달력을 구현할 때는 fsCalendar라는 라이브러리 사용을 많이 합니다.
사실 결론적으로 저도 프로젝트를 진행하면서 결국은 fsCalendar를 사용했지만 초반에는 커스텀으로 직접 달력을 구현해보려고 했었습니다.

그 당시에 만들었던 코드를 공유하고자 합니다. :)

우선 당시 제 프로젝트 구조는 아래와 같습니다.

  1. 뷰컨 위에 테이블뷰가 있다.
  2. 테이블 뷰 셀 안에 컬렉션뷰가 존재한다.
  3. 컬렉션뷰로 달력을 구현한다.

따라서 컬렉션 뷰 셀 코드는 아래와 같습니다.

import UIKit

class CalendarCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var containerView: UIView!
    @IBOutlet weak var dateLbl: UILabel!
}

셀 안에 컨테이너 뷰로 뷰 하나를 만들어놓고 해당 뷰 내부에 라벨 하나를 넣어주었습니다.
그리고 아래 코드는 테이블뷰 셀 안에서 직접 달력을 그려주는 코드들이 진행되어집니다.

class WeekTableViewCell: UITableViewCell {
    let now = Date()
    var cal = Calendar.current
    let dateFormatter = DateFormatter()
    var components = DateComponents()
    var weeks: [String] = ["일", "월", "화", "수", "목", "금", "토"]
    var days: [String] = []
    // 해당 월이 몇일까지 있는지
    var daysCountInMonth = 0
    // 시작일
    var weekdayAdding = 0   

    @IBOutlet weak var yearMonthLbl: UILabel!
    @IBOutlet weak var collectionView: UICollectionView!

    override func awakeFromNib() {
        super.awakeFromNib()

        initUI()
    }

    func initUI() {
        self.collectionView.delegate = self
        self.collectionView.dataSource = self
        self.collectionView.isScrollEnabled = false

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0

        self.collectionView.collectionViewLayout = layout

        dateFormatter.dateFormat = "yyyy년 M월"
        components.year = cal.component(.year, from: now)
        components.month = cal.component(.month, from: now)
        components.day = 1

        self.calculation()
        self.collectionView.reloadData()
    }

    /*
     1 일요일 2 - 1  -> 0번 인덱스부터 1일 시작
     2 월요일 2 - 2  -> 1번 인덱스부터 1일 시작
     3 화요일 2 - 3  -> 2번 인덱스부터 1일 시작
     4 수요일 2 - 4  -> 3번 인덱스부터 1일 시작
     5 목요일 2 - 5  -> 4번 인덱스부터 1일 시작
     6 금요일 2 - 6  -> 5번 인덱스부터 1일 시작
     7 토요일 2 - 7  -> 6번 인덱스부터 1일 시작
     */
    func calculation() {
        let firstDayOfMonth = cal.date(from: components)
        // 해당 수로 변환 > 1은 일요일 ~ 7은 토요일
        let firstWeeakDay = cal.component(.weekday, from: firstDayOfMonth!)
        daysCountInMonth = cal.range(of: .day, in: .month, for: firstDayOfMonth!)!.count
        // 이 과정을 해주는 이유는
        // 예로 2020년 4월이라하면 4월 1일은 수요일, 수요일이 달의 첫 날이 됨
        // 수요일은 components의 4이기 때문에 collectionView에서 앞의 3일은 비울 필요가 있음
        // 따라서 index가 1일부터 시작할 수 있도록 해줌
        // 따라서 2-4해서 -2부터 시작하게 되어 정확히 3일후부터 1일이 시작
        weekdayAdding = 2 - firstWeeakDay

        self.yearMonthLbl.text = dateFormatter.string(from: firstDayOfMonth!)

        self.days.removeAll()

        for day in weekdayAdding...daysCountInMonth {
            // 1보다 작은경우는 비워줘야함
            if day < 1 {
                self.days.append("")
            } else {
                self.days.append(String(day))
            }
        }
    }

    @IBAction func prevBtnClicked(_ sender: UIButton) {
        components.month = components.month! - 1
        self.calculation()
        self.collectionView.reloadData()
    }


    @IBAction func nextBtnClicked(_ sender: UIButton) {
        components.month = components.month! + 1
        self.calculation()
        self.collectionView.reloadData()
    }
}

extension WeekTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        switch section {
        case 0:
            // 요일 수는 고정
            return 7
        default:
            // 일의 수
            return self.days.count
        }
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "CalendarCell", for: indexPath) as! CalendarCollectionViewCell

        switch indexPath.section {
        case 0:
            // 요일
            cell.dateLbl.text = weeks[indexPath.row]
        default:
            // 일
            cell.dateLbl.text = days[indexPath.row]
        }

        if indexPath.row % 7 == 0 {
            cell.dateLbl.textColor = .red
        } else if indexPath.row % 7 == 6 {
            cell.dateLbl.textColor = .blue
        } else {
            cell.dateLbl.textColor = .white
        }

        return cell
    }
}