티스토리 뷰

이번에 만들 UI는 명칭이 좀 다양한데요.

사이드바, 메뉴바, 햄버거 메뉴, 슬라이드바 등등 부르는 이름이 다양한데 

 

일단 편의상 사이드 메뉴(Side Menu) 라고 부르겠습니다. 

또한 만드는 방법이 다양한 만큼 두번으로 나누어 올리도록 하겠습니다. 

 

1탄 수작업으로 사이드바 만들기  (with Code)

2탄 라이브러리를 사용해서 제작하기 (SideMenu)

 

그래서 이번엔 라이브러리를 사용하지 않고 만들어볼까 합니다.

 

참고 사이트

이번에도 다른 분의 포스트를 참고하여 만들었습니다. 

(테이블 뷰까지 같이 넣어 만드실 분은 아래 사이트가 더 유용하실꺼에요!)

https://johncodeos.com/how-to-create-a-side-menu-in-ios-using-swift/

 

How to create a Side Menu in iOS using Swift | John Codeos

Create a custom side menu without using any third-party library. We make two types of menus (Slide Out and On Top) with the same codebase.

johncodeos.com

아 참고로 제가 만들어 확인하고 싶은건 저 메뉴판(?)의 위치나 크기 잡는 법이기 때문에 

메뉴버튼이나 테이블을 넣지않고 라벨만 몇개 넣어 만들겠습니다. 

*저는 위 코드에서 이해를 위해 최대한 간단하게 표현하는걸 목표로 했습니다.*

위 글을 처음부터 다 이해하기에는 저한테 너무 어렵..

 

저희는 일반 뷰(UIViewController) 위에 뷰(UIViewController)가 나오도록 만들겠습니다.

 

일단 그럼 어떤 뷰를 보여줄지 만들어야겠죠? 

 

기본 뷰 설정 

1. 메인뷰에 해당 메뉴를 나오게할 버튼을 하나 만들어주세요. 

- 반대쪽 버튼은 다음 포스트에서 사용할려고 만든겁니다.

저기 저 버튼을 누르면 나오도록 만들꺼에요.

2. 나오게 할 뷰를 만들어 주세요. (저는 가로 260의 뷰가 나오게 만들께요.)

- 뷰 컨트롤러 하나를 만든 다음 아래 설정 항목을 통해 크기를 조절해주세요. 

여기 설정을 바꾸시면 되요.

3. 뷰 위에 원하시는 버튼이나 보이게 하고 싶은 UI를 추가해주세요.

- 저는 버튼 몇개만 추가해서 아래처럼 보이게 만들께요. 

저거 그냥 다 버튼이에요.

- 하시고 나서 클래스 연결하는거 잊지 말아주세요.

StoryBoard ID 를 반드시 추가해주세요.

코드 작성하기 

1. 메인 뷰를 ViewController / 메뉴창을 SideMenuViewController 라고 하겠습니다.

class ViewController: UIViewController {
    override public func viewDidLoad() {
        super.viewDidLoad()     
    }
}
class SideMenuViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

 

2. 메인뷰(ViewController)에 아래 코드를 추가해주세요. (자세한 사항은 코드 주석을 참고해주세요.)

import UIKit

class ViewController: UIViewController {
    // 사이드바 컨트롤러
    private var sideMenuViewController: SideMenuViewController!
    //사이드바 그림자 영역
    private var sideMenuShadowView: UIView!
    //사이드바 가로 넓이 + 회전시 폭
    private var sideMenuRevealWidth: CGFloat = 260
    private let paddingForRotation: CGFloat = 150
    //열려있는가
    private var isExpanded: Bool = false
    // 사이드메뉴 열기/닫기 설정용 LC
    private var sideMenuTrailingConstraint: NSLayoutConstraint!
   
    override public func viewDidLoad() {
        super.viewDidLoad()
		// 사이드바 추가 
        self.setBasicSideMenu()
    }

    // 수동으로 버튼 누르면 만든 뷰 열기
    @IBAction func openSlideMenuPro(_ sender: Any) {
        self.sideMenuState(expanded: self.isExpanded ? false : true)
    }
    
    //MARK: - 수동제작 사이드 메뉴 관련 코드들
    func setBasicSideMenu(){
        // Shadow Background View
        self.sideMenuShadowView = UIView(frame: self.view.bounds)
        self.sideMenuShadowView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.sideMenuShadowView.backgroundColor = .black
        self.sideMenuShadowView.alpha = 0.0
        // 그림자 영역 클릭시 창 닫기
        let closeShadowClick = UITapGestureRecognizer(target: self, action: #selector(sideMenuState(tapRecog:)))
        self.sideMenuShadowView.addGestureRecognizer(closeShadowClick)
        // 그림자 영역 추가
        view.addSubview(self.sideMenuShadowView)

        // Side Menu - 사이드 메뉴 연결 이름 주의하세요!
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        self.sideMenuViewController = storyboard.instantiateViewController(withIdentifier: "SideMenuViewController") as? SideMenuViewController
   
        // 사이드바 영역 추가
        view.addSubview(self.sideMenuViewController!.view)
        addChild(self.sideMenuViewController!) // 뷰가 추가될 영역 설정
        self.sideMenuViewController!.didMove(toParent: self)

        // 사이드 메뉴 레이아웃 잡기
        self.sideMenuViewController.view.translatesAutoresizingMaskIntoConstraints = false
        self.sideMenuTrailingConstraint = self.sideMenuViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -self.sideMenuRevealWidth - self.paddingForRotation)
        self.sideMenuTrailingConstraint.isActive = true
    
        NSLayoutConstraint.activate([
            self.sideMenuViewController.view.widthAnchor.constraint(equalToConstant: self.sideMenuRevealWidth),
            self.sideMenuViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            self.sideMenuViewController.view.topAnchor.constraint(equalTo: view.topAnchor)
        ])
    }
    // 그림자 부분 클릭시 닫기 연결용
    @objc func sideMenuState(tapRecog: UITapGestureRecognizer){
        // 열려있으면 닫기 
        self.sideMenuState(expanded: self.isExpanded ? false : true)
    }
    // 액션 설정 부분
    func sideMenuState(expanded: Bool) {
        if expanded {
            self.animateSideMenu(targetPosition:  0 ) { _ in
                self.isExpanded = true
            }
            // Animate Shadow (Fade In)
            UIView.animate(withDuration: 0.5) { self.sideMenuShadowView.alpha = 0.6 }
        }
        else {
            self.animateSideMenu(targetPosition: (-self.sideMenuRevealWidth - self.paddingForRotation)) { _ in
                self.isExpanded = false
            }
            // Animate Shadow (Fade Out)
            UIView.animate(withDuration: 0.5) { self.sideMenuShadowView.alpha = 0.0 }
        }
    }
    
    func animateSideMenu(targetPosition: CGFloat, completion: @escaping (Bool) -> ()) {
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0, options: .layoutSubviews, animations: {
            self.sideMenuTrailingConstraint.constant = targetPosition
            self.view.layoutIfNeeded()
        }, completion: completion)
    }
}

- 해당 코드는 참고 사이트에서 어려운 부분을 잘라서 최대한 간단하게 바꾼 겁니다.

- 그래서 참고 사이트에 있는 코드와 많은 부분이 유사합니다. 

 

여기까지 하시면 아래처럼 보이게 됩니다. 

잘 나오시나요?

메뉴에서 선택하는 부분은 일반 버튼을 연결하듯이 처리하셔도 되고 

아니면 protocal 을 통해 main 에서 처리해주셔도 됩니다. 

 

이 부분은 편하신 방법을 사용하시면 될것같아요. 

 

그럼 다음번에는 라이브러리를 사용하는 방법으로 돌아오겠습니다. 

오늘도 파이팅입니다.

댓글