티스토리 뷰

계속해서 실습니다.

 

접근성 메뉴의 폰트 크기 변경시 업데이트


앱 실행 도중 접근성 메뉴에서 더 큰 텍스트 변경후

앱으로 돌아왔을시 숫자 및 기호가 커지질 않습니다.

변경 후 카드를 다시 그리지 않아 발생한 문제입니다.

해당 설정이 변경된 경우 탐지하는 함수에서 setNeetDisplay 호출이 필요합니다.

 

traitCollectionDidChange 함수를 쓰면 뷰에서 확인이 가능합니다.

파라미터로 UITraitCollection가 전달되며 해당 객체로, 회전하는지 가로모드인지, 위의 폰트 설정등을 확일 할 수 있습니다.

해당 함수에서 다시 그리도록 코드를 추가합니다.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    
    setNeedsLayout()
    setNeedsDisplay()
}

실행 후 옵션을 변경해보면 정상적으로 변경되는 것을 확인 할 수 있습니다.

 

화면 비율 정하기


현재 카드는 디바이스 크기에 맞춰져 있어 실제 카드 비율과 차이가 있습니다.

이를 개선해봅시다.

먼저 뷰에 적용된 제약사항들을 확인합시다

인터페이스 빌더 화면에서 Playing Card View를 누른 뒤 우측 상단 탭에서

Size Inspector를 누릅니다(삼각형 모양)

우측 가운데쯤에서 적용되어 있는 Constraints를 확인 할 수 있습니다.

4개의 제약 조건을 아래와 같이 수정합니다.

16보다 크거나 같다로 수정합니다.

수정후에는 빨간색 선들이 생깁니다.

제약 조건이 모호해졌기 때문입니다.

에러 내용들입니다.

 

일단은 에러처리는 두고 비율을 지정해보도록 합시다.

5:8 비율로 지정을 해봅시다.

컨트롤 드래그하여 자신에서 놓은 뒤 Aspect Ratio를 선택합시다.

아니면 뷰를 선택 후 아래에 존재하는  Add New Contraint 메뉴에서 추가가 가능합니다.

Aspect Ratio를 선택 후 Add 1 Constraint 버튼을 눌러줍니다.

 

제약 조건이 추가된 것을 확인 할 수 있습니다.

비율은 사용자마다 다를수 있습니다.

원하는 비율인 5:8로 변경합시다.

Edit 버튼을 누른 뒤 5:8로 변경 후 Enter를 입력합니다.

 

추가적으로 수평, 수직의 중앙 위치를 설정합시다.

크기를 줄이고 가운데로 이동 후 컨트롤 드래그를 통하여 상위 View에 놓습니다.

나타나는 제약 조건에서 Center Horizontally/Vertical in Safe Area를 적용합시다.

command + 클릭으로 제약조건 다중 선택이 가능합니다.

그래도 아직 빨간 선으로 나타나고 있습니다.

크기를 모르기 때문인데요 이제 크기를 지정해봅시다.

최대한 큰 크기로 지정해보록 합시다.

먼저 너비 제약 조건을 추가 합시다.

뷰를 클릭 후 드래그 하여 자신에게 높은 뒤 Width를 추가합니다.

높이가 비율에 맞춰 자동으로 조절됩니다. 그리고 빨간선들도 제거되었습니다.

에러들이 제거되었지만 여전이 크기가 작습니다. 더 큰 크기로 변경해봅시다.

임의의 너비인 800으로 변경해봅시다.

다시 빨간선들이 생깁니다. 추가된 모든 제약 조건을 충족시킬 수 없기 때문입니다.

Safe Area 내부에 존재해야한다는 조건들 4개와 수평,수직 위치에 대한 조건 2개는 필수 적이나

너비는 필수 제약이 아닙니다. 그냥 크게 적용하고 싶어서 넣은 제약조건입니다.

즉 우선 순위가 낮다고 볼 수 있습니다. 

이것을 수정해봅시다.

우선 순위 값이 1000이 제일 높으며 임의로 숫자값을 입력할 수 있으나 몇몇 값(750,250)들은 제공해줍니다.

width 제약조건의 우선 순위 값을 750으로 변경합시다.

우선순위를 750으로 설정합시다. 다른 제약 조건보다 우선 순위가 낮은것이지 적용이 안되는 것은 아닙니다.

 

이미지 추가하기 및 그리기


카드 이미지를 추가하고 그려봅시다.

각 모양에 대한 J,Q,K 카드 이미지가 필요하고 뒤집어진 상태에서 표시할 이미지도 필요합니다.

아래 주소에서 다운로드 가능합니다.

https://drive.google.com/drive/folders/1RSCJ5zj3ehxrDoGtUnqFp7Kw9wOvj6Lg

다운 받은 후 이미지를 추가해봅시다.

프로젝트 네비게이션에서 Assets를 누른 뒤 파일을 선택 후 아래와 같이 이동시킵니다.

이제 추가 되었으니 사용해봅시다.

PlayingCardView.swift 파일을 선택한 뒤 Draw(rect)를 수정합시다.

 override func draw(_ rect: CGRect) {
    let roundRect = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
    roundRect.addClip()
    UIColor.white.setFill()
    roundRect.fill()
        
    if let faceCardImage = UIImage(named:"\(rankString)\(suit)") {
        faceCardImage.draw(in: bounds.zoom(by: SizeRatio.faceCardImageSizeToBoundsSize))
    }
}

UIImage(named:) 함수를 통하여 이미지를 생성할 수 있습니다.

이름은 Assets에 추가된 이름입니다.(ex.K♦️)

UIImage 객체를 생성했으면 draw(in:)함수를 통하여 그려줍니다.

bound를 사용하면 가득차는 문제로 크기를 약간 축소하여 그려줬습니다.

 

숫자가 1~10인 경우에도 그려봅시다.

일단 그리는 코드 작성시 시간이 많이 소모되므로 수업내에서 제공을 해줍니다.

더보기
private func drawPips()
{
    let pipsPerRowForRank = [[0], [1], [1,1], [1,1,1], [2,2], [2,1,2], [2,2,2], [2,1,2,2], [2,2,2,2], [2,2,1,2,2], [2,2,2,2,2]]
    
    func createPipString(thatFits pipRect: CGRect) -> NSAttributedString {
        let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { max($1.count, $0)})
        let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { max($1.max() ?? 0, $0)})
        let verticalPipRowSpacing = pipRect.size.height / maxVerticalPipCount
        let attemptedPipString = centeredAttributedString(suit, fontSize: verticalPipRowSpacing)
        let probablyOkayPipStringFontSize = verticalPipRowSpacing / (attemptedPipString.size().height / verticalPipRowSpacing)
        let probablyOkayPipString = centeredAttributedString(suit, fontSize: probablyOkayPipStringFontSize)
        if probablyOkayPipString.size().width > pipRect.size.width / maxHorizontalPipCount {
            return centeredAttributedString(suit, fontSize: probablyOkayPipStringFontSize /
                (probablyOkayPipString.size().width / (pipRect.size.width / maxHorizontalPipCount)))
        } else {
            return probablyOkayPipString
        }
    }
    
    if pipsPerRowForRank.indices.contains(rank) {
        let pipsPerRow = pipsPerRowForRank[rank]
        var pipRect = bounds.insetBy(dx: cornerOffset, dy: cornerOffset).insetBy(dx: connerString.size().width, dy: connerString.size().height / 2)
        let pipString = createPipString(thatFits: pipRect)
        let pipRowSpacing = pipRect.size.height / CGFloat(pipsPerRow.count)
        pipRect.size.height = pipString.size().height
        pipRect.origin.y += (pipRowSpacing - pipRect.size.height) / 2
        for pipCount in pipsPerRow {
            switch pipCount {
            case 1:
                pipString.draw(in: pipRect)
            case 2:
                pipString.draw(in: pipRect.leftHalf)
                pipString.draw(in: pipRect.rightHalf)
            default:
                break
            }
            pipRect.origin.y += pipRowSpacing
        }
    }
}

draw(rect) 함수를 더 수정해봅시다.

faceUp인 상황에서는 pips를 그려주도록하고

faceDown인 경우에는 카드 뒷면을 그리도록 했습니다.

override func draw(_ rect: CGRect) {
    let roundRect = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
    roundRect.addClip()
    UIColor.white.setFill()
    roundRect.fill()
        
    if isFaceUp {
        if let faceCardImage = UIImage(named:"\(rankString)\(suit)") {
            faceCardImage.draw(in: bounds.zoom(by: SizeRatio.faceCardImageSizeToBoundsSize))
        }
        else {
            drawPips()
        }
    }
    else {
        if let cardBackImage = UIImage(named:"cardback") {
            cardBackImage.draw(in: bounds)
        }
    }
}

 

인터페이스 빌더에 변수 추가하기


현재 카드가 정상적으로 렌더링 되는지 확인하기 위해서는 아래 변수의 값들을 변경시킨 뒤 실행을 해야합니다.

class PlayingCardView: UIView {
    
    var rank: Int = 8 { didSet { setNeedsDisplay(); setNeedsLayout() }}
    var suit: String = "♥️" { didSet { setNeedsDisplay(); setNeedsLayout() }}
    var isFaceUp: Bool = true { didSet { setNeedsDisplay(); setNeedsLayout() }}

	...
}

매번 rank나 suit, isFaceUp을 수정한 뒤 실행을 해야하는데 손쉽게 확인이 가능하도록 수정해봅시다.

사실 인터페이스 빌더 내에서 바로 확인이 가능합니다.

@IBDesignable 키워드를 class 앞에 추가합시다.

@IBDesignable
class PlayingCardView: UIView{

}

 

그런뒤 인터페이스 빌더에서 확인해보면 아래와 같이 나옵니다.

흰색인 이유는 뒷면이기 때문입니다.

 흰색 네모로 나오는데 이유는 현재 isFaceUp 변수가 false로 되어 있어 뒷면이 나오기 때문입니다.

뒷면 이미지가 나와야하는데 일반적인 상황에서는 인터페이스 빌더에서 나오지 않고 추가 작업이 필요합니다.

isFaceUp 변수를 true로 바꾼뒤 확인해보면 아래와 같이 정상적으로 출력됩니다.

 

이미지도 나오도록 수정해봅시다.

UIImage를 생성하는 코드를 수정해야합니다.

생성시 사용하는 함수는 UIImage(named: Sting in: Bundle?, compatibleWith: UITraitCollection?) 입니다.

아래와 같이 수정합시다.

override func draw(_ rect: CGRect) {
    let roundRect = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
    roundRect.addClip()
    UIColor.white.setFill()
    roundRect.fill()
    
    if isFaceUp {
        if let faceCardImage = UIImage(named:"\(rankString)\(suit)",
                                       in:Bundle(for:self.classForCoder),
                                       compatibleWith: traitCollection) {
            faceCardImage.draw(in: bounds.zoom(by: SizeRatio.faceCardImageSizeToBoundsSize))
        }
        else {
            drawPips()
        }
    }
    else {
        if let cardBackImage = UIImage(named:"cardback",
                                       in:Bundle(for:self.classForCoder),
                                       compatibleWith: traitCollection) {
            cardBackImage.draw(in: bounds)
        }
    }
}

코드 적용 후 인터페이스 빌더로 이동하면 이미지가 정상적으로 출력되는것을 확인 할 수 있습니다.

뒷면으로 표시했을시

이제 빌드를 안해도 인터페이스 빌더에서 바로 확인이 가능합니다.

하지만 코드에서 수정하는건 여전히 불편합니다.

보다 개선해봅시다.

인터페이스 빌더에서 변수값들을 변경할 수 있으면 좋을것 같습니다.

Content Mode나 Background와 같은 변수처럼요.

Attribute Inspecter내의 몇몇 변수들을 바로 수정 할 수 있습니다.

 @IBInspectable 키워드를 사용하면 변수 추가가 가능합니다.

아래와 같이 rank, suit, isFaceUp 앞에  @IBInspectable 키워드를 추가해봅시다.

class PlayingCardView: UIView {
    
    @IBInspectable var rank: Int = 12 { didSet { setNeedsDisplay(); setNeedsLayout() }}
    @IBInspectable var suit: String = "♥️" { didSet { setNeedsDisplay(); setNeedsLayout() }}
    @IBInspectable var isFaceUp: Bool = false { didSet { setNeedsDisplay(); setNeedsLayout() }}
    
    ...
}

추가한 후 인터페이스 빌더에서 PlayingCardView를 누르고 attribute Inspector를 확인하면

우측 상단에 변수들이 추가되어 있습니다.

해당 값들을 변경하면 바로 적용되는 것을 확인 할 수 있습니다.

Rank를 12로 변경해봅시다.

바로 적용이 되는것을 확인 할 수 있습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함