꾸준히 안타치기

메모장 만들기(스토리보드) +배경이미지 + 얼럿창이미지추가 본문

iOS/기본편 | 실전편 -꼼꼼한재은씨

메모장 만들기(스토리보드) +배경이미지 + 얼럿창이미지추가

글자줍기 2022. 2. 6. 16:05
반응형

스토리보드 화면구성

메모 목록화면 구성 

프로젝트를 생성하고, 메인스토리보드를 연다.

기본적으로 생성되어있는 viewController.swift와 메인스토리보드의 뷰컨트롤러를 삭제한다. ->Move to Trash

 

빈스토리 보드에 네비게이션 컨트롤러를 추가한다.(테이블 뷰 컨트롤러가 자동으로 추가됨)

프로토타입 셀이 선택된 상태에서 인스펙터 탭 -> 높이값 설정  | 테이블뷰전체셀 높이도 동일하게 

빈스토리 보드에 네비게이션 컨트롤러를 추가한다.(테이블 뷰 컨트롤러가 자동으로 추가됨)

테이블뷰 전체를 선택하고 프로토타입 셀 값을 2로 설정 / 셀의 갯수가 2개를 의미한다.

- 텍스트만 있는 셀1, 텍스트+이미지가 있는셀 2개를 만들것 

 


메모작성화면 구성

목록에 BarButton Item을 끌어서 + 버튼을 추가한다.

새로운 뷰컨트롤러를 추가하고 Ctrl + 마우스 드래그하여 세그웨이를 연결한다. 타입은 SHOW

글작성 컨트롤러에 네비게이션 아이템을 추가하고, 타이틀을 글 작성으로 변경한다.

 

네비게이션바 우측에 BarButtonItem을 추가하고 Title속성을 저장으로 바꾼다. 카메라버튼도 추가

텍스트뷰, 이미지 뷰를 추가한다.


상세화면구성  /  메모목록에서 눌러서 들어간 화면

새로운 뷰컨트롤러를 추가, 목록에서 상세화면으로 이동하는 메뉴얼 세그웨이를 연결한다.  > show타입

목록화면의 노랑색원을 ctrl 누르고 끌어서 상세화면으로 연결한다.

세그를 클릭하고,  Identifier값을 read_sg로 작성(세그웨이 호출 식별값)

화면에 제목과 내용 레이블, 이미지 뷰를 추가한다.

내용 레이블 line값을 0으로 설정 -> 라인수 제약X , LineBreak -> Word Wrap으로 설정

 

이미지뷰의 높이 값을 선택한다.

세로부분 클릭,  하이트이퀄스 부분 클릭하고 들어가서, 표시부분 체크해준다.

- 이미지 종류에 따라 실제 높이를 변경가능하도록, 높이값은 빌드타임에 제거 되도록 Remove at build time 설정을 추가 

네비게이션바에 Navigation item을 추가하고 상세화면으로 변경


커스텀 클래스 생성 및 객체연결

메모 목록화면 클래스  / TextimgListVC.swift

코코아터치 클래스 > UITableVIewController 로 파일 생성

메모목록화면 스토리보드와 TextimgListVC.swift 클래스를 연결한다.

MemoCell클래스를 추가 /  UITableViewCell 

메모목록화면에서 프로토타입셀을 2개 생성했으므로 두개다  MemoCell클래스를 추가한다.

 

데이터 모델 작업 - 데이터모델은 사용자가 입력한 데이터를 저장하고, 목록이나 상세화면에 출력할 수 있도록 데이터 소스역할을 하는 객체를 의미한다.

주로 클래스로 정의하며, 여러 속성을 프로퍼티로 정의한다. 

// 기능을 구현을 위해 제일먼저 할일 데이터모델을 정의 한다.
// 데이터 소스역할을 하는 객체 생성하기 
import UIKit

class MemoData {
    var memoIdx : Int? // 데이터 식별값
    var title : String? // 메모제목
    var contents : String? // 메모내용
    var image: UIImage? // 이미지
    var regdate: Date? // 작성일
}

AppDelegate.swift파일을 열고 , MemoData타입의 배열변수 memolist를 프로퍼티로 정의한다.

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var memolist = [MemoData]() // 메모데이터를 저장할 배열변수

MemoData객체를 [MemoData]()배열 변수에 쌓는다.( 일종의 공용 데이터 저장소 , 앱의 여러객체가 참조한다.)

AppDelegate클래스는 전역변수를 저장하기에 적당하다. 이클래스는 앱전체의 라이프사이클을 관리하는 앱 델리게이트 역할을 하기 때문에, 앱내에서 반드시 하나의 인스턴스만 존재하도록 시스템적으로 보장되어있고, 어디서든 쉽게 접근할 수 있다.

앱이 생성될때 함께 생성되었다가, 앱이 소멸할때 함께 소멸된다. 도중에 소멸되거나 생성되지 않는다. 한번 생성되면 앱이 종료되기 전까지는 계속 유지된다. 앱이 사용할 데이터를 저장해두면, 데이터 유실을 걱정하지 않아도 된다. 다만, 앱 종료시에는 사라진다.

메모작성화면 클래스 / MemoFormVC

- 글제목, 내용, 이미지를 카메라버튼을 눌러 갤러리에서 선택하고, 저장

- 저장버튼을 누르면, 글제목,내용,이미지 내용을  AppDelegate에 만든배열에 추가 후, 이전화면으로 돌아감

- 글제목은 글내용을 입력함과 동시에 같은 내용으로 동시에 작성됨

- 내용을 작성하지 않을경우 alert창 생성

- 이미지피커,텍스트 뷰는 델리게이트 패턴기반으로 동작함.

- viewDidLoaddp 버튼 배경이미지, 줄간격 추가함 @@@

-  저장버튼 얼럿창에 이미지추가 @@@

- 화면터치시 네비바 안보이게 하기 추가@@@

import UIKit

class MemoFormVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate , UITextViewDelegate{
    
    var subject: String! // 글제목

    @IBOutlet weak var contents: UITextView! // 텍스트작성영역
    @IBOutlet weak var preview: UIImageView!// image 미리보기
    
    // 카메라버튼
    @IBAction func pick(_ sender: Any) {
        // 이미지 피커 인스턴스를 생성한다.
        let picker = UIImagePickerController()
        
        picker.delegate = self // 이미지피커컨트롤러 인스턴스의 델리게이트 속성 현재뷰 컨트롤러 인스턴스로설정
        picker.allowsEditing = true // 피커이미지편집 허용
        
        // 이미지피커 화면을 표시한다.
        self.present(picker, animated: true)
    }
    
    // 사용자가 이미지를 선택하면 자동으로 이 메소드가 호출된다.
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        // 선택된이미지를 미리보기에 출력한다.
        self.preview.image = info[.editedImage] as? UIImage
        
        // 이미지 피커 컨트롤러를 닫는다.
        picker.dismiss(animated: false)
    }
    
    override func viewDidLoad() {
        self.contents.delegate = self
        
        // 배경이미지 설정 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        let bgImage = UIImage(named: "memo-background.png")!
        self.view.backgroundColor = UIColor(patternImage: bgImage)
        
        //텍스트뷰의 기본 속성
        self.contents.layer.borderWidth = 0
        self.contents.layer.borderColor = UIColor.clear.cgColor
        // 배경 투명한색으로 설정
        self.contents.backgroundColor = UIColor.clear
        
        // 텍스트 줄 간격
        let style = NSMutableParagraphStyle()
        style.lineSpacing = 9
        self.contents.attributedText = NSAttributedString(string: "", attributes:[.paragraphStyle:style])
        self.contents.text = ""
    }
    
    // 사용자가 텍스트뷰에 뭔가 입력을 하면 자동으로 이 메소드 호출
    func textViewDidChange(_ textView: UITextView) {
        // 내용의 최대 15자리 까지 읽어서 subject변수에 저장한다.
        let contents = textView.text as NSString
        // 읽어온 내용이 15자리보다 크면, 15까지만 ,        그보다 짧을경우 전체내용을 읽어온다.
        let length = ((contents.length > 15) ? 15: contents.length)
        // 최대 15자리까지의 내용을 subject변수에 저장한다. 이값이 제목이 됨 / substring 원하는범위만 잘라냄
        self.subject = contents.substring(with: NSRange(location: 0, length: length))
        
        // 네비게이션 타이틀에 표시한다.
        self.navigationItem.title = self.subject
    }
    
    // 저장버튼
    // 경고창 이미지추가@@@
    @IBAction func save(_ sender: Any) {
         // 경고창에 사용될 콘텐츠 뷰 컨트롤러 구성 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        let alertV = UIViewController()
        let iconImage = UIImage(named: "warning-icon-60")
        alertV.view = UIImageView(image: iconImage)
        alertV.preferredContentSize = iconImage?.size ?? CGSize.zero
        
        
        // 1.내용을 입력하지 않았을 경우,경고한다.
        guard self.contents.text?.isEmpty == false else {
            let alert = UIAlertController(title: nil, message: "내용을 입력해주세요", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            // 경고창이미지추가 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            alert.setValue(alertV, forKey: "contentViewController")
                            self.present(alert, animated: true)
                            return
        }
        // 2.MemoData 객체를 생성하고, 데이터를 담는다.아까만들었던 데이터모델객체
        let data = MemoData() //
        
        data.title = self.subject // 제목
        data.contents = self.contents.text // 내용
        data.image = self.preview.image // 이미지
        data.regdate = Date() // 작성시간
        
        // 3.앱델리게이트 객체를 읽어온 다음, 앱델리게이트에 정의한 memolist 배열에 MemoData객체를 추가한다.
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.memolist.append(data)
        
        // 4.작성폼화면을 종료하고, 이전화면으로 돌아간다.(popviewController 이전화면으로 가는 메소드)
        _ = self.navigationController?.popViewController(animated: true)
        
    }
}

화면터치시 네비바 안보이게 하기 추가@@@

    // 화면터치시 네비게이션바 안보이게 하기 
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        let bar = self.navigationController?.navigationBar
        
        let ts = TimeInterval(0.3)
        UIView.animate(withDuration: ts){
            bar?.alpha = (bar?.alpha == 0 ? 1 : 0)
        }
    }

MemoCell클래스

import UIKit

class MemoCell: UITableViewCell {

    @IBOutlet weak var subject: UILabel! //글제목
    @IBOutlet weak var contents: UILabel!//글내용
    @IBOutlet weak var regdate: UILabel! //날짜시간
    @IBOutlet weak var img: UIImageView!// 이미지
}

메모 목록화면 클래스  / TextimgListVC.swift

import UIKit

class TextimgListVC: UITableViewController {
    
    //앱 델리게이트 객체의 참조 정보를 읽어온다.
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    
    
    //테이블의 셀 개수를 결정하는 메소드
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        let count = self.appDelegate.memolist.count
        return count
    }
    
    // 요청하는 행의 정보를 가져옴
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //1.memolist 배열 데이터에서 주어진 행에 맞는 데이터를 꺼낸다.
        let row = self.appDelegate.memolist[indexPath.row]
        
        //2.이미지 속성이 비어있을경우, memoCell 혹은 memoCellWithImage 를 사용
        let cellId = row.image == nil ? "memoCell" : "memoCellWithImage"
        
        //3.재사용 큐로부터 프로토타입 셀의 인스턴스를 전달받는다.
//        let cell = tableView.dequeueReusableCell(withIdentifier: cellId) as! MemoCell
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId) as! MemoCell
        
        //4.memoCell의 내용을 구성한다.
        cell.subject?.text = row.title
        cell.contents?.text = row.contents
        cell.img?.image = row.image
        
        //5.Date타입의 날짜를 yyyy-MM-dd HH:mm:ss 포맷에 맞게 변경한다.
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        cell.regdate?.text = formatter.string(from: row.regdate!)
        
        //6.cell 객체를 리턴한다.
        return cell
    }
    
    //행을 선택했을때 호출되는 메소드
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // memoList 배열에서 선택된 행에 맞는 데이터를 꺼낸다.
        let row = self.appDelegate.memolist[indexPath.row]
        
        //상세화면의 인스턴스를 생성한다.
        guard let vc = self.storyboard?.instantiateViewController(withIdentifier: "MemoRead") as? MemoReadVC else {
            return
        }
        //값을 전달한 다음,상세화면으로 이동한다.
        vc.param = row
        self.navigationController?.pushViewController(vc, animated: true)
        
    }
    
    //디바이스 스크린에 뷰 컨트롤러가 나타낼때 마다 호출되는 메소드
    override func viewWillAppear(_ animated: Bool) {
        //테이블 데이터를 다시 읽어들인다. 이에 따라 행을 구성하는 로직이 다시 실행될 것이다.
        self.tableView.reloadData()
    }
    
}

 

상세화면구성  /  메모목록에서눌러서 들어간 화면 / MemoReadVC.swift

스토리보드 아이디를 MemoRead로  작성

제목, 내용, 이미지 아울렛을 연결해 변수 생성

MemoData 타입의 변수 param을 상단에 정의

import UIKit

class MemoReadVC: UIViewController {
    
    // 콘텐츠 데이터를 저장하는 변수
    var param: MemoData?

    @IBOutlet weak var subject: UILabel!// 제목
    @IBOutlet weak var contents: UILabel! //내용
    @IBOutlet weak var img: UIImageView!// 이미지뷰
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

       // 제목,내용,이미지를 출력
        self.img.image = param?.image
        self.subject.text = param?.title
        self.contents.text = param?.contents
      
        // 이미지가 왜 안보일까?
        print("이미지 : \( String(describing: self.img.image) )")
        
        // 날짜포맷변환
        let formatter = DateFormatter()
        formatter.dateFormat="dd일 HH:mm분에 작성됨"
        let dateString = formatter.string(from: (param?.regdate)!)
        
        //네비게이션 타이틀에 날짜를 표시
        self.navigationItem.title = dateString
    }
}

TextimgListVC.swift로 돌아가서  TableView(_:didSelectRowAt:)메소드에 작성한다.

    //행을 선택했을때 호출되는 메소드
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // memoList 배열에서 선택된 행에 맞는 데이터를 꺼낸다.
        let row = self.appDelegate.memolist[indexPath.row]
        
        //상세화면의 인스턴스를 생성한다.
        guard let vc = self.storyboard?.instantiateViewController(withIdentifier: "MemoRead") as? MemoReadVC else {
            return
        }
        //값을 전달한 다음,상세화면으로 이동한다.
        vc.param = row
        self.navigationController?.pushViewController(vc, animated: true)
    }

 

반응형
Comments