ViewController에서 버튼을 누르면 다른 ViewController로 이동하는,, ViewController간 데이터 전달은 간단하다.

 

아래 처럼 왼 쪽 ViewController의 'Next Button'을 눌러 오른 쪽 하늘 색 배경의 SecondViewController로 이동하고자 하는 경우, 아래 코드로 쉽게 구현할 수 있다.

단, 코드에서 SecondViewController에 접근하기 위해, 오른쪽 인스펙터 영역에 보이는 것 처럼 Class와 Storyboard ID를 등록해주어야 한다.

이렇게 구현하는 방법을 present 방식이라 하는데, viewController간 화면 전환은 이 방법 말고도 다양하다.

 

.

.

.

 

본론으로 들어가서, 내가 작업하고 있는 상황은 위와는 조금 다르다.

 

ViewController 위에  CollectionViewCell이 있고, CollectionViewCell 위에 각각의 셀을 따로 swift 파일로 만들어서 작업하고 있었다. 말이 좀 복잡한데,, 다음과 같은 상황이다.

ViewController 위에 CollectionView가 있다. 그 위에 FirstCollectionViewCell을 추가해주었다. (Cell 등록 및 구현 방법은 코드 참고)

실행시키면 다음과 같이 나타난다.

CollectionView의 Section0에 FirstCollectionViewCell이 추가 되었다. 해당 셀의 Next Button을 눌렀을 때 Second View Controller로 화면이 이동하는 것이 목표이다.

 

간단히 정리하자면 ViewController가 FirstCollectionViewCell의 버튼 기능을 위임 받으면 된다. 이를 'Delegate Pattern'이라 칭한다. 한 객체가 다른 객체의 기능을 위임 받아 대신 동작할 수 있도록 하는 것이다.

 

 

 

 

Delegate Pattern을 적용하는 방법

 

1. 위임을 요청할 메소드를 프로토콜로서 정의한다.

   - 'Next Button'을 눌렀을 때의 동작을 위임할 것이므로 다음과 같이 정의해준다.

// ViewController.swift
protocol CollectionViewCellDelegate {
    func nextButtonDidTap(index: Int)
}

 

2. 위임받을 동작에 대한 함수를 구현 해준다.

   - 'Next Button'을 눌렀을 때 Main 스토리보드에 있는 SecondViewController로 이동하도록 한다. 맨 처음 ViewController간  화면전환 구현 코드와 동일하다. "SecondVC" 자리에 스토리보드 아이디를 입력하면 된다.

// ViewController.swift
extension ViewController: CollectionViewCellDelegate {
    func nextButtonDidTap(index: Int) {
        let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondVC") as! SecondViewController
        secondViewController.modalPresentationStyle = .fullScreen
        self.present(secondViewController, animated: true)
    }
}

 

 

2. index 프로퍼티를 생성하고 index 값을 넘겨준다.

// FirstCollectionViewCell.swift
    @IBOutlet weak var nextButton: UIButton!
    
    var index: Int = 0
    var delegate: CollectionViewCellDelegate?
    
    @IBAction func nextButtonDidTap(_ sender: Any) {
        self.delegate?.nextButtonDidTap(index: index)
    }

 

 

3. 셀 생성시 프로토콜 변수의 레퍼런스를 ViewController로 설정하고 해당 셀의 index를 위임 받을 cell의 index 프로퍼티로 설정한다.

   - 이 부분은 정확히 이해 못했는데... 셀을 등록하고 나서 주석 처리 부분 사이의 코드 두 줄을 입력해서 index를 넘겨주는 것 같다.

// ViewController.swift
func collectionView(_ collectionView: UICollectionView,
                    cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: FirstCollectionViewCell.identifier,
            for: indexPath) as? FirstCollectionViewCell else {
                fatalError()
            }

	// 이 부분!
        cell.index = indexPath.row
        cell.delegate = self
        //

        return cell
}

 

끝!!!!!

쫘잔..

 

 

참고 링크

https://juhyeoklee.github.io/ios/ios-post06/

 

[iOS] Delegation Pattern 을 이용한 Cell 안의 버튼 이벤트 처리

AppJam 회고

juhyeoklee.github.io

 

인스타그램에서 상대방 프로필에 들어갔을 때, 아무 게시글도 올리지 않았을 경우 'No posts yet'이라는 텍스트와 함께 뜨는 화면이 있다. 그런 화면을 'empty view'라 하는 것 같다.

 

나의 경우 컬렉션 뷰를 여러 섹션으로 나눠서 각 섹션 별로 셀을 다루는 작업을 하고 있었는데, 특정 섹션에서 셀 개수가 0일 때 empty view를 띄우는 것이 목적이었다.

이런 느낌.. section 2의 cell 개수가 0일 때 empty view 띄우는 것이 목적

특정 섹션의 셀 개수에 접근하는 부분을 코드로 어떻게 작성해야할 지 몰라 헤맸는데, 생각보다 간단했다.

// Declaration
func numberOfItems(inSection section: Int) -> Int

 

(해당 컬렉션 뷰 이름).numberOfItems(inSection: (해당 섹션 번호))

 

로 접근하면 된다.

 

실제로 사용할 땐 다음과 같이 if문을 사용하여 해당 섹션의 데이터 수가 0일 경우 이미지 뷰의 isHidden 값을 false로 바꾸어 화면에 나타나게 할 수 있다. 이미지 뷰는 컬렉션 뷰 자체에 구현해주면 된다.

if (profileCollectionView.numberOfItems(inSection: 2) == 0) {
            no_posts.isHidden = false
        }

 

그러면 다음과 같이 나타난다.

 

섹션1 색상을 하늘색으로 넣으려 했는데 실수로 뷰컨트롤러와 같은 색인 연보라 색으로 넣어서 티가 안나네.......중요한 건 아니니까 뭐..

셀 개수가 1개 이상일 때

 

셀 개수가 0개일 때

 

 

 

 

참고 링크

https://developer.apple.com/documentation/uikit/uicollectionview/1618016-numberofitems

 

Apple Developer Documentation

 

developer.apple.com

https://mini-min-dev.tistory.com/115

 

[iOS] 아무 데이터가 없을 때 나오는 화면,엠티뷰(empty view) 만들기

iOS 개발을 하면, 가장 자주 만들어야 할 화면이 바로 테이블 뷰와 컬렉션 뷰일 거다. 테이블 뷰(TableView)와 컬렉션 뷰(CollectionView)는 모두 같은 형태의 데이터를 표출할 때 큰 틀만 만들어두고, 그

mini-min-dev.tistory.com

 

https://www.acmicpc.net/problem/1748

 

1748번: 수 이어 쓰기 1

첫째 줄에 N(1 ≤ N ≤ 100,000,000)이 주어진다.

www.acmicpc.net

문제 요약

1부터 100,000,000 사이의 정수 N을 하나 입력 받는다.

1부터 N까지 수를 이어 써서 만든 새로운 숫자의 자릿수를 출력하면 된다.

예를 들어, 15를 입력한 경우 새로운 숫자는 123456789101112131415이 되고, 자릿수는 21이 된다.

 

풀이

시행착오를 많이 겪었다. 여러가지 풀이를 떠올렸는데, 다양한 이유로 자꾸 틀렸다.

 

1. queue 이용 -> 메모리 초과

가장 처음엔 단순하게 1부터 N까지의 숫자를 queue에 담아 size를 출력하는 풀이를 생각했다.

두자리 수 이상의 수를 q에 나누어 담는 방법은 숫자를 10으로 계속 나누어 가며 나머지를 찾으면 된다. 

메모리 초과로 틀린 풀이긴 하지만 나는 이런 식으로 했다.

//1748. 수 이어 쓰기 1
//queue 이용 -> 메모리 초과
#include <iostream>
#include <queue>
using namespace std;
const int MAX = 100000008;

int main() {
    int N; scanf("%d", &N);
    queue<int> q;
    for (int i = 1; i <= N; i++) {
        int j = i;
        while (j) {
            q.push(j % 10);
            j /= 10;
        }
    }
    printf("%d", q.size());
}

 

2. 변수 선언하여 자릿수만큼 ++해주기 -> 시간 초과

큐를 안 쓰는 방법으로 int형 변수 count를 선언하여 자릿수를 세도록 다음과 같이 코드를 수정했는데,

이번엔 시간 초과가 떴다.

//1748. 수 이어 쓰기 1
//변수 선언하여 자릿수만큼 ++해주기 -> 시간 초과
#include <iostream>
using namespace std;

int main() {
    int N; scanf("%d", &N);
    int count = 0;
    for (int i = 1; i <= N; i++) {
        int j = i;
        while (j) {
            count++;
            j /= 10;
        }
    }
    printf("%d", count);
}

 

3. 점화식 찾기

위 풀이처럼 입력한 숫자만큼 for문을 돌면서 자릿수를 세면 안되는 것 같다.

숫자를 입력하면 그에 해당하는 출력값을 바로 뱉어내는 계산식이 필요하겠다는 생각이 들어, 수학적으로 접근했다.

 

  (a) 입력 받은 수의 자릿수(digit)를 계산한다.

  (b) (digit - 1)자리 까지의 자릿수는 정해진 값이므로 계산하여 ans에 더한다.

   1부터 9까지의 수는 1자리 수가 총 9개 -> 9 * 10^0 * 1

   10부터 99까지의 수는 2자리 수가 총 90개 -> 9 * 10^1 * 2

   100부터 999까지의 수는 3자리 수가 총 900개 -> 9 * 10^2 * 3

  (c) N(입력 받은 수)까지 나머지 수들을 ans에 더한다.

 

예를 들어 입력 값이 120인 경우,

  (a) digiit = 3이 된다.

  (b) 120 = (1~9) + (10~99) + (100~120) 이렇게 나눌 수 있는데, (1~9) + (10~99) 까지의 합이 b 과정이다.

  (c) 나머지 수 100부터 120까지의 합을 구하여 ans에 더한다. 

 

이 방식으로 풀었을 때 test case도 다 잘 돌아갔는데 자꾸 틀렸다고 나왔다.

식에는 틀린 부분이 없는 것 같은데 왜 자꾸 틀리지 해서 ans를 int형이 아닌 long형으로 바꿔주니 맞았다.

//1748. 수 이어 쓰기 1
//3. 점화식 찾기
#include <iostream>
#include <cmath>
using namespace std;

int main() {
    int N; scanf("%d", &N);
    int digit = 0, tmp = N;
    long ans = 0;
    
    // (a)
    while (tmp) {			
        digit++;	
        tmp /= 10;
    }
    
    // (b)
    for (int i = 0; i < digit - 1; i++) {			
        ans += 9 * pow(10, i) * (i + 1);
    }
    
    // (c)
    ans += (N - (pow(10, digit - 1) - 1)) * digit;		
    
    printf("%ld\n", ans);
}

 

4. 효율적인 코드

다음과 같은 코드가 가장 효율적인 것 같다.

for문을 한 번 돌 때마다 1의 자리 수, 10의 자리 수, 100의 자리 수,,, 와 같이 세서 count에 더해준다.

//1748. 수 이어 쓰기 1
#include <iostream>
using namespace std;

int main() {
    int N; scanf("%d", &N);
    int count = 0;
    for (int i = 1; i <= N; i *= 10) count += N - i + 1;
    printf("%d", count);
}

 

'BOJ' 카테고리의 다른 글

[c++] 백준 1966번: 프린터 큐  (0) 2022.03.16
[c++] 백준 4949번: 균형잡힌 세상  (0) 2022.01.28
[c++] 백준 4153번: 직각삼각형  (0) 2022.01.21

https://www.acmicpc.net/problem/1966

 

1966번: 프린터 큐

여러분도 알다시피 여러분의 프린터 기기는 여러분이 인쇄하고자 하는 문서를 인쇄 명령을 받은 ‘순서대로’, 즉 먼저 요청된 것을 먼저 인쇄한다. 여러 개의 문서가 쌓인다면 Queue 자료구조에

www.acmicpc.net

풀이

queue와 priority_queue를 사용하여 푸는 문제다.

 

예제 입력의 마지막 테스트 케이스를 예로 들면,

6 0
1 1 9 1 1 1

이므로 6개의 문서 중요도 중 0번째에 위치한 1의 출력 순서가 몇 번째인지를 출력하면 된다.

해당 문서의 중요도 뿐 만 아니라 위치(index값)도 고려해야하기 때문에, 큐에 구조체를 사용해서 중요도와 index를 함께 저장할 것이다.

큐에 구조체를 쓸 생각을 못해서 풀이 시간이 오래 걸렸다....

 

먼저 우선순위 큐를 사용하여 중요도를 내림차순으로 정렬한다.

9 1 1 1 1 1

 

큐에는 index와 중요도가 함께 저장된다.

index: 0 1 2 3 4 5

importance: 1 1 9 1 1 1

 

큐와 우선순위 큐의 맨 앞 값을 비교하여 일치하지 않을 경우 큐의 front 값을 pop하여 뒤로 push한다.

1 9 1 1 1 1

9 1 1 1 1 1

위와 같이 정렬 되어 큐와 우선순위 큐의 맨 앞 값이 9로 일치하면, pop해준다. 

1이 출력될 때까지 반복한다.

 

코드

#include <iostream>
#include <queue>

using namespace std;

struct st {
    int index;  //몇 번째인지 저장
    int importance;    //중요도 저장
};

int main() {
    int t; scanf("%d", &t);  //테스트 케이스 개수
    for (int i = 0; i < t; i++) {
        int N, M; scanf("%d %d", &N, &M);    //N: 문서 개수, M: 몇 번째로 인쇄되었는지 궁금한 문서가 놓인 위치(맨 왼쪽 0)

        priority_queue<int> p_q;    //내림차순 정렬
        queue<st> q, turn_q, turn;      //q: 숫자 저장, turn_q: 찾고자하는 문서 위치를 turn에 저장하기 위한 큐, turn: 찾고자 하는 문서의 index와 importance 저장

        for (int i = 0; i < N; i++) {
            int I; scanf("%d", &I);     //I: 중요도
            q.push({i, I});
            turn_q.push({i, I});
            p_q.push(I);
        }

        for (int i = 0; i < M; i++) {
            turn_q.push(q.front());
            turn_q.pop();
        }
        turn.push(turn_q.front());

        int count = 0;
        while(!q.empty()) {
            if (q.front().importance == p_q.top()) {
                if (q.front().index == turn.front().index) {
                    printf("%d\n", count + 1);
                    break;
                }
                else {
                    count++;
                    q.pop();   
                    p_q.pop(); 
                }
            }
            else {
                q.push(q.front());
                q.pop();
            }
        }
    }
}

'BOJ' 카테고리의 다른 글

[c++] 백준 1748번: 수 이어 쓰기 1  (0) 2022.03.21
[c++] 백준 4949번: 균형잡힌 세상  (0) 2022.01.28
[c++] 백준 4153번: 직각삼각형  (0) 2022.01.21

https://www.acmicpc.net/problem/4949

 

4949번: 균형잡힌 세상

하나 또는 여러줄에 걸쳐서 문자열이 주어진다. 각 문자열은 영문 알파벳, 공백, 소괄호("( )") 대괄호("[ ]")등으로 이루어져 있으며, 길이는 100글자보다 작거나 같다. 입력의 종료조건으로 맨 마

www.acmicpc.net

풀이

스택을 이용해 푸는 문제. 9012번과 비슷해서 쉽게 풀릴 줄 알았는데 괄호의 종류가 많아지면서 풀이할 때 헤맸다. 괄호가 (소괄호)와 [대괄호] 두 종류이니 스택도 두 개 필요하겠지라고 단순히 생각했다가 한참 헤맸다.

 

입력받은 한 문장을 for문으로 읽어들이며 (, ), [, ] 네 가지 문자를 만날 때 마다 적절한 조건을 걸어 스택에 넣거나 빼주면 된다.

(와 [의 경우 추가 조건 없이 스택에 push해준다. 단, ( ]와 같이 짝지어질 경우 틀린 것이므로 두 괄호의 종류를 구분하여 push한다.

)와 ]는 각각 (, ]와 짝을 이루면 pop해서 스택에서 제거한다.

 

"yes"를 출력하기 위해서는 입력받은 한 문장을 for문을 돌며 끝까지 읽어야하기 때문에, "no"를 출력하는 경우를 먼저 찾아 거른다.

)를 입력받은 경우를 예로 들면, 스택이 비어있거나 스택의 top이 (가 아닌 경우 "no"를 출력하고 for문을 종료하여 다음 새로운 문장을 입력받는다. 위 조건에 걸리지 않고 for문이 종료되어도 스택이 비어있지 않다면 "no"를 출력한다. 위 조건을 모두 만족한 경우 "yes"를 출력한다.

 

코드

#include <iostream>		//getline 함수 (\n까지 문자열 입력 받음)   
#include <stack>
using namespace std;

int main() {
    while (1) {  
        stack<int> st;
        string tmp; getline(cin, tmp);
        if (tmp == ".") return 0;
        int flag = 0;

        for (int i = 0; i < (int)tmp.size(); i++) {
            if (tmp[i] == '(') st.push('(');

            else if (tmp[i] == '[') st.push('[');
        
            else if (tmp[i] == ')') {
                if (st.empty() || st.top() != '(') {    //st가 비어있거나 top이 )일 경우
                    flag = 1;
                    break;
                }
                else if (!st.empty() && st.top() == '(') st.pop();
            }

            else if (tmp[i] == ']') {
                if (st.empty() || st.top() != '[') {
                    flag = 1;
                    break;
                }
                else if (!st.empty() && st.top() == '[') st.pop();
            }
        }
        if (flag) printf("%s\n", "no");
        else if (st.size()) printf("%s\n", "no");
        else printf("%s\n", "yes");
    }
}

 

 

'BOJ' 카테고리의 다른 글

[c++] 백준 1748번: 수 이어 쓰기 1  (0) 2022.03.21
[c++] 백준 1966번: 프린터 큐  (0) 2022.03.16
[c++] 백준 4153번: 직각삼각형  (0) 2022.01.21

+ Recent posts