문제 링크 - https://school.programmers.co.kr/learn/courses/30/lessons/120875

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제 설명

x,y 좌표값으로 구성된 점 4개의 정보가 2차원 정수형 배열로 주어짐

4개의 점들이 조합되어 만들 수 있는 직선 중 서로가 평행한 경우가 있다면 결과값으로 1을, 아니라면 0을 반환해야함

 

 

풀이법 구상

점의 정보는 4개로 고정된 만큼만 주어짐

직선의 x,y 좌표간 차이값의 절대값이 같으면 평행한 것으로 간주하고 풀었다가 틀림 -> 방향 고려 안됨

방향을 포함하는 기울기값(y차이 / x차이)이 같으면 평행

 

주어진 점 4개로 서로 다른 직선을 구성할 수 있는 경우의 수를 아래와 같음

(0번째 점, 1번째 점)

(0번째 점, 2번째 점)

(0번째 점, 3번째 점)

(1번째 점, 2번째 점)

(1번째 점, 3번째 점)

(2번째 점, 3번째 점)

 

서로 다른 직선 6가지 경우의 각 기울기 값을 구하여 반복문을 돌며 같은 값이 존재하는지 판단 -> 틀림(12번 케이스부터 실패)

 

위와 같은 로직으로 기울기들을 판별할 경우 불가능한 경우까지 비교하게됨

ex. (0번째 점, 1번째 점)으로 이루어진 직선과 (0번째 점, 2번째 점)으로 이루어진 직선 간의 기울기를 비교

 

즉 주어진 점 4개로 '서로 다른 2개의 직선'을 구성하여 비교하기 위한 경우는 아래와 같음

(0번째 점, 1번째 점) VS (2번째 점, 3번째 점)

(0번째 점, 2번째 점) VS (1번째 점, 3번째 점)

(0번째 점, 3번째 점) VS (1번째 점, 2번째 점)

 

 

코드 구현 (스위프트 사용)

func solution(_ dots:[[Int]]) -> Int {
    if (getInc(dots[0], dots[1]) == getInc(dots[2], dots[3])) {
        return 1
    } else if  (getInc(dots[0], dots[2]) == getInc(dots[1], dots[3])) {
        return 1
    } else if  (getInc(dots[0], dots[3]) == getInc(dots[1], dots[2])) {
        return 1
    } else {
        return 0
    }
}

func getInc(_ m:[Int], _ n: [Int]) -> Double {
    let diffX = max(m[0], n[0]) - min(m[0], n[0])
    let diffY = max(m[1], n[1]) - min(m[1], n[1])
    let inc = ((n[0]-m[0])*(n[1]-m[1]) < 0) ? -1.0 : 1.0
    return inc*(Double(diffY)/Double(diffX))
}

 

문제 링크 - https://www.acmicpc.net/problem/2012

 

2012번: 등수 매기기

첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 500,000) 둘째 줄부터 N개의 줄에 걸쳐 각 사람의 예상 등수가 순서대로 주어진다. 예상 등수는 500,000 이하의 자연수이다.

www.acmicpc.net


 

문제 설명

n명에게 각각 예상 등수값을 입력받고, 그 값들을 토대로 불만족 지수가 최소로 나오도록 구성하여 불만족 지수의 최소값을 출력

불만족 지수란 예상 등수가 1등이고, 실제 순위 구성 후 등수가 3등인 경우 (|1-3|)으로 2의 값이 됨

(n의 범위는 1이상 500,000이하)

 

풀이법 구상

예상 순위보다 실제 순위가 높든 낮든 무관하게 그 차이값만큼이 불만족 수치가 됨

최대한 예상 순위가 실제 순위가 되도록 해야하므로 정렬 후 각 예상 순위값을 돌며 실제 순위와의 차이값을 구해야함

정렬 + 반복문이 필요함

-> 스위프트 기본 정렬 메소드의 시간복잡도 - O(n log n)

-> 반복문 시간 복잡도 - O(N)

-> 동시에 일어나지 않고 정렬 후 반복문 돌게됨

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ arr: [Int]) -> Void {
    var sum = 0
    let temp = arr.sorted()
    for i in 1...arr.count { sum += abs(i - temp[i-1]) }
    print(sum)
}

var arr: [Int] = []
let cnt = Int(readLine()!)!
for _ in 0..<cnt { arr.append(Int(readLine()!)!) }

solution(arr)

구현 결과

 

구현 코드

https://github.com/ryr0121/UIKitPractice/tree/main/StackViewPractice

 

사용한 라이브러리

Snapkit, Then

 

구현 과정

구현에 참고한 이미지

1. 구현 영역 분리

- 모서리가 둥근 전체 컨테이너 역할의 UIView

- 좌측 상단의 도시명, 현재 기온을 포함한 UIStackView (수직 방향)

- 우측 상단의 구름 이미지, 날씨, 최고/최저 기온을 포함한 UIStackView (수직 방향)

- 하단의 각 시간대별 날씨를 포함한 UIStackView (수평 방향)

 

 

2. 사용될 UI 컴포넌트 정의

2-1. 모서리가 둥근 전체 컨테이너 역할의 UIView 정의

lazy var backView = UIView().then {
    $0.backgroundColor = .gray
    $0.layer.cornerRadius = 20
    $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapWeatherView)))
}

 

2-2. 좌측 상단의 도시명, 현재 기온을 포함한 수직 UIStackView

 

lazy var leftTitleLabel1 = getLabelView(titleStr: "서울특별시", fontSize: 20.0, fontWeight: .bold, fontColor: .white)
lazy var leftTitleLabel2 = getLabelView(titleStr: "9°", fontSize: 40.0, fontWeight: .medium, fontColor: .white)

lazy var leftStackView = UIStackView().then {
    $0.backgroundColor = .green
    
    // 스택 요소로 포함할 subView들을 선언
    let stackView = UIStackView(arrangedSubviews: [leftTitleLabel1, leftTitleLabel2])
    
    $0.axis = .vertical		// 수직 방향으로 스택을 구성
    $0.distribution = .fill	// 요소들이 스택을 채울 수 있게 분포되도록 구성
    $0.alignment = .leading	// 왼쪽 끝에 붙어서 요소가 나열되도록 구성
    $0.spacing = 0		// 요소 간의 간격 0
}

 
// helper methods - 컨테이너 뷰를 가진 UILabel 컴포넌트를 반환
func getLabelView(titleStr: String, fontSize: Double, fontWeight: UIFont.Weight, fontColor: UIColor) -> UIView {
    let titleLabel = UILabel().then {
        $0.text = titleStr
        $0.font = .systemFont(ofSize: fontSize, weight: fontWeight)
        $0.textColor = fontColor
    }
    let containerView = UIView().then {
        $0.backgroundColor = .systemPink
        $0.backgroundColor = .clear
    }

    containerView.addSubview(titleLabel)
    titleLabel.snp.makeConstraints { make in
        make.top.bottom.leading.trailing.equalToSuperview()
    }
    return containerView
}

 

2-3. 우측 상단의 구름 이미지, 날씨, 최고/최저 기온을 포함한 수직 UIStackView

let cloudImgView = UIImageView(image: UIImage(systemName: "cloud.fill")).then {
    $0.tintColor = .white
}

// getLabelView는 상단 helper method 참조
lazy var rightTitleLabel1 = getLabelView(titleStr: "대체로 흐림", fontSize: 18.0, fontWeight: .medium, fontColor: .white)
lazy var rightTitleLabel2 = getLabelView(titleStr: "최고: 21° 최저 7°", fontSize: 18.0, fontWeight: .medium, fontColor: .white)
    
lazy var rightStackView = UIStackView().then {
    $0.backgroundColor = .blue
    let stackView = UIStackView(arrangedSubviews: [cloudImgView, rightTitleLabel1, rightTitleLabel2])
    $0.axis = .vertical
    $0.distribution = .fill
    $0.alignment = .trailing	// 요소들이 오른쪽 끝에 붙어 나열될 수 있도록 구성
    $0.spacing = 3
}

 

좌우측 상단에 스택뷰를 추가한 후

 

2-4. - 하단의 각 시간대별 날씨를 포함한 수평 UIStackView 

let weatherWithTimeList = [
    ["오전 8시", "10°"],
    ["오전 9시", "12°"],
    ["오전 10시", "15°"],
    ["오전 11시", "17°"],
    ["오후 12시", "19°"],
    ["오후 1시", "20°"],
]

lazy var weatherViews: [UIView] = []

// helper method - 시간 및 기온 정보 배열을 토대로 하단 수평 스택뷰의 요소로 쓰이는 UIView 반환
// viewDidLoad 내에서 호출하여 weatherViews 배열 구성
func configureWeatherOfTime() {
    weatherWithTimeList.map { info in
        weatherViews.append(getWeatherViewOfTime(timeStr: info[0], temper: info[1]))
    }
}

lazy var bottomStackView = UIStackView().then {
    let stackView = UIStackView(arrangedSubviews: weatherViews)
    $0.axis = .horizontal	// 수평 방향으로 요소가 나열되도록 지정
    $0.distribution = .fillEqually	// 모든 요소가 스택 내에서 동등한 크기를 가지도록 지정
    $0.alignment = .leading
    $0.spacing = 5
}

하단 스택뷰를 추가한 후

 

 

3. 레이아웃 구성

private func setLayouts() {
    // add views
    self.view.addSubview(backView)

	// 전체 컨테이너 뷰에 좌측 상단, 우측 상단, 하단 스택뷰를 추가
    backView.addSubview(leftStackView)
    backView.addSubview(rightStackView)
    backView.addSubview(bottomStackView)

    // add views in StackView
    // 좌측 상단 스택뷰 내에 요소로 사용될 sub view들을 추가
    leftStackView.addArrangedSubview(leftTitleLabel1)
    leftStackView.addArrangedSubview(leftTitleLabel2)

    // 우측 상단 스택뷰 내에 요소로 사용될 sub view들을 추가
    rightStackView.addArrangedSubview(cloudImgView)
    rightStackView.addArrangedSubview(rightTitleLabel1)
    rightStackView.addArrangedSubview(rightTitleLabel2)

    // 하단 스택뷰 내에 요소로 사용될 sub view들을 추가
    weatherViews.map { view in
        bottomStackView.addArrangedSubview(view)
    }

    // set constraints
    backView.snp.makeConstraints { make in
        make.top.equalToSuperview().offset(130)
        make.leading.equalToSuperview().offset(20)
        make.trailing.equalToSuperview().inset(20)
    }

    leftStackView.snp.makeConstraints { make in
        make.leading.top.equalToSuperview().offset(15)
    }

    cloudImgView.snp.makeConstraints { make in
        make.top.equalToSuperview()
    }
    rightStackView.snp.makeConstraints { make in
        make.top.trailing.equalToSuperview().inset(15)
        make.bottom.equalTo(leftStackView.snp.bottom)
    }
    bottomStackView.snp.makeConstraints { make in
        make.top.equalTo(leftStackView.snp.bottom).offset(30)
        make.leading.trailing.equalToSuperview().inset(10)
        make.bottom.equalToSuperview().inset(15)
    }
}

 

4. 추가) 수직 스택뷰 요소 간 spacing 달리 주기

// 구름 이미지 UIImageView를 감싸는 컨테이너 뷰 정의
let cloudImgContainerView = UIView().then {
    $0.backgroundColor = .clear
}

let cloudImgView = UIImageView(image: UIImage(systemName: "cloud.fill")).then {
    $0.tintColor = .white
    $0.contentMode = .scaleAspectFill
}

// 레이아웃 구성 변경
cloudImgView.snp.makeConstraints { make in
    make.top.bottom.leading.trailing.equalToSuperview()
}

// 구름 이미지 컴포넌트 하단 요소에 대해 top 방향으로 원하는 만큼의 여백값을 추가
rightTitleLabel1.snp.makeConstraints { make in
    make.top.equalTo(cloudImgView.snp.bottom).offset(20)
}

 

5. 추가) 전체 컨테이너 영역 내에 대한 탭 이벤트 정의

lazy var backView = UIView().then {
    $0.backgroundColor = .gray
    $0.layer.cornerRadius = 20
    
    // 탭 제스처를 감지하는 객체를 추가
    $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapWeatherView)))
}

@objc func didTapWeatherView() {
    let detailVC = WeatherDetailViewController()
    self.navigationController?.pushViewController(detailVC, animated: true)
}

*참고*

SceneDelegate.swift 파일 내 willConnectTo 메소드

코드 베이스로 UIKit을 사용할 경우,

가장 처음 시작점이 되는 ViewController 내에서 NavigationController를 사용하기 위해서는 

해당 ViewController가 UINavigationContoller를 가질 수 있도록 정의해주어야 함

 

 

 

 

구현 결과

 

구현 코드

https://github.com/ryr0121/UIKitPractice/tree/main/CustomCollectionViewPractice

 

구현 과정

1. Storyboard에 CollectionView 추가

컴포넌트 추가 단축키 - cmd + shift + L

 

2. 추가한 CollectionView를 @IBOutlet으로 연결

 

3. CollectionView에 사용할 커스텀 Cell을 Xib으로 생성 및 정의

  3-(1) CollectionViewCell을 xib으로 생성

파일 추가 단축키 - cmd + N
"Also create XIB file"을 반드시 체크해주어야 .xib 생성됨
생성 완

  3-(2) Cell 내부 정의

우측에 있는 width / height로 셀의 크기를 조정하여 스토리보드 사용 가능
파일을 듀얼로 띄우기 - option을 누른 채로 왼쪽 파일 탭에서 띄울 파일을 클릭

 

4. CollectionView를 사용하기 위한 코드 추가 (프로토콜 채택, 커스텀 셀 등록 등)

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupCollectionView()
    }

    func setupCollectionView() {
        // CollectionView를 사용하기 위한 필수 프로토콜 채택
        collectionView.dataSource = self
        collectionView.delegate = self
        // CollectionView에 사용할 커스텀 셀 등록
        collectionView.register(UINib(nibName: "MainCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MainCollectionViewCell")
    }
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // CollectionView에 보여질 셀의 개수 반환
        return 10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // CollectionView에 보여질 셀 반환
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCollectionViewCell", for: indexPath) as? MainCollectionViewCell else { return UICollectionViewCell() }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // 셀의 크기 반환
        return CGSize(width: 100, height: 100)
    }
}

 

 

추가

Storyboard에서 CollectionView의 스크롤 방향 조정하기

Vertical 수직 스크롤 (기본값)
Horizontal 수평 스크롤

 

* 한 줄짜리 수평 스크롤이 가능한 CollectionView를 만들기 위해서는, CollectionView의 높이가 셀의 높이의 2배보다 작아야 함

높이를 130으로 지정한 수평 스크롤이 가능한 CollectionView

구현 결과

구현 코드 - https://github.com/ryr0121/UIKitPractice/tree/main/TableViewPracticeWithStoryboard

 

구현 과정

1. Storyboard에 TableView 추가

컴포넌트 추가 단축키 - cmd + shift + L

 

2. 추가한 TableView를 @IBOutlet으로 연결

 

3. TableView에 사용할 TableViewCell을 xib파일로 정의

   (1) xib 파일 생성하기

새 파일 생성 단축키 - cmd + N
"Also create XIB file"을 반드시 체크!!!해주어야만 .xib이 생성됨
생성 완.

 

   (2) xib 파일 내에 컴포넌트 추가 및 레이아웃 구성

파일 듀얼로 띄우는 법 - option을 누른 채로 옆에 띄울 파일을 클릭

 

4. TableView를 사용하기 위한 사전 작업을 포함한 코드 추가 (프로토콜 채택, 커스텀 셀 등록 등)

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()커

        setupTableView()
    }
    
    func setupTableView() {
        // TableView를 사용하기 위해 필수적인 프로토콜 채택
        tableView.delegate = self
        tableView.dataSource = self
        // TableView에 커스텀 셀을 사용하기 위한 사전 등록
        tableView.register(UINib(nibName: "MainTableViewCell", bundle: nil), forCellReuseIdentifier: "MainTableViewCell")
    }
}
class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        setupTableView()
    }
    
    func setupTableView() {
        // TableView를 사용하기 위해 필수적인 프로토콜 채택
        tableView.delegate = self
        tableView.dataSource = self
        // TableView에 커스텀 셀을 사용하기 위한 사전 등록
        tableView.register(UINib(nibName: "MainTableViewCell", bundle: nil), forCellReuseIdentifier: "MainTableViewCell")
    }
}

컬렉션 뷰 셀 내에 세부 내용을 보여줄 라벨과 날짜를 보여줄 라벨 두 가지를 추가하고

날짜 라벨은 날짜 딱 한 줄만 보여주도록, 내용 라벨은 여러줄에 걸쳐 보여줄 수 있도록 제약조건 설정을 해주었다 (각각 모두 12로 맞춰줌)

이 때 hugging priority 관련의 제약조건 오류가 생기는데, Content Hugging Priority가 두 라벨 모두 같은 값이고, 셀 내의 제약조건에 따라 요소들을 위치시키려고 하자니, 어떤 요소를 더 늘려서 맞춰넣어야 하는지 애매해져버려 발생하는 오류인 것 같다

 

Content Hugging Priority값이 더 낮으면, 그 값이 더 높은 요소 대신에 크기가 늘려져서 제약조건에 맞도록 설정된다

값이 더 높은 요소는 정해진 조건값에 따라 그 크기를 유지할 수 있게 된다

 

 

내용 라벨의 Content Hugging Priority 값을 1 낮추어서, 날짜 라벨의 그 값보다 더 낮도록 설정해주면 아래와 같이 오류가 해결된다

 

 


content compression resistance priority는 요소가 가지는 크기가 커지는 경우에 대해, 찌그러질 것이냐 말것이냐에 대한 우선순위를 정하는 것이다

이미지에서의 컬렉션뷰 셀과 같이 정해진 범위내에서 만약 내용 라벨에 담길 컨텐츠가 많아지면, 내용 라벨이 자신의 크기를 줄이던지, 혹은 날짜 라벨이 줄어들던지 해야할 것이다

이 경우에 대해, content compression resistance priority값이 날짜 라벨이 더 높게 설정된다면 날짜 라벨은 크기를 유지하고, 값이 더 낮은 내용 라벨이 찌그러지게(크기가 줄어들게) 되는 것이다

 


 

일단 내가 이해한 바로는,,,

Content Hugging Priority

       -> 자리 남는다; 누가 몸집 더 키울래? (값 높은 애 : 전 아닙니다 / 값 낮은 애 : 아휴 제가 늘릴게요;)

Content Compression Resistance Priority
      -> 뭐야 자리 부족하잖아; 누가 찌그러질래? (값 높은 애 : 전 아니라고요 / 값 낮은 애 : 예예 저요)

+ Recent posts