블로그 이미지
매몰

모바일 어플리케이션 개발 1인 기업 고영진모바일입니다. 저와 함께 맛깔스러운 앱을 만들어 보아요~

Rss feed Tistory
개발/ios 2018.07.30 15:07

스레드에서 반복문으로 UI를 변경할때 주의할점



내가 만든 스레드에서 UI를 바꿀수 없다는것은 누구나 알것이다. 물론 젖먹이 개발자 시절에 난 몰랐다...


어잿든 내 스레드에서 UI를 바꿀려면 메인스레드를 호출해야 한다. ios 에서는 DispatchQueue.main.async 이 그 역활을 한다.


짐작했겠지만 메인스레드는 새로 만들어지는게 아니므로 당연히 동기적으로 움직인다. 


최근까지도 이 사실을 자주 망각했다. 바로 반복문에서 메인스레드를 호출할때였다.


내 스레드에서 메인스레드를 반복문으로 돌릴때는 서로가 따로 놀기 때문에 (내 스레드와 메인스레드가 서로 비동기라서) 메인스레드에서 UI가 아직 변경되지도 않았는데 내 스레드에서 또 호출해 버린다. 이렇게 되면 버벅거리는 현상이 발생한다.


테스트할때서야 버벅거림을 발견하고 짜증내다 아... 하고 숙연해 진적이 한둘이 아니었다.


아래를 보면 스레드에서 ui를 10번 갱신한다.

하지만 apply변수를 뺀다면 10번이 다 갱신 되지 않고 중간에 몇번은 건너뛰어 결국 일부만 실행되는것 처럼 보일것이다.



//스레드 시작

Thread(target: self, selector: #selector(runLoop), object: nil).start()



//스레드 실행 함수

@objc func runLoop() {

    var apply = true

    var count = 0

        

    while count < 10 {

        //메인스레드에서 작업이 완료되었을때만 실행

        if apply {

            //시작

            apply = false

            count += 1

                

            DispatchQueue.main.async {

                //ui 변경

                //...

                setNeedsDisplay()

                    

                //완료

                apply = true

            }

        }

    }

}




쉽게 말해 동기화를 해야 한다는 말이다.


알면서도 실수할수 있으니 항상 유념하자.



수제앱장인


 

 

CEO

Developer

S/W Enginner

고영진


실패만 하고 있어도 꿈을 포기하지 않는 남자
제가 직접 경험하고 습득한 지식을 위주로 올릴게요





사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2018.05.08 11:12

UIEdgeInsets 없이 UILabel에 여백 넣기

UILabel에 여백을 넣는 방법은 다양하다.

그 중 NSLayoutConstraint를 이용하는 법을 다뤄보자!






class FitLabel: UILabel {


    //상하좌우 여백 storyboard 입력값

    @IBInspectable var edge: CGSize = CGSize(width: 0, height: 0)

    

    //너비, 높이가 적용될 Constraint

    private var mWidthConstraint, mHeightConstraint: NSLayoutConstraint?

    

    //좌우 여백값

    var edgeAllWidth: CGFloat {

        return edge.width * 2

    }

    

    //상하 여백값

    var edgeAllHeight: CGFloat {

        return edge.height * 2

    }

    

    override func awakeFromNib() {

        super.awakeFromNib()

        

        //storyboard에서 초기화된 글자로 맞춤

        fit()

    }


    func set(fitText text: String) {

        //코드상에서 글자를 직접 넣어 맞춤

        self.text = text

        fit()

    }

    

    private func fit() {

        //글자 + 여백 크기

        let width = intrinsicContentSize.width + edgeAllWidth

        let height = intrinsicContentSize.height + edgeAllHeight


        if mWidthConstraint == nil && mHeightConstraint == nil {

            //첫 실행때만 Constraint를 추가

            

            //Constraint를 직접 추가하기 위해 오토사이징 끔

            translatesAutoresizingMaskIntoConstraints = false

            

            //위의 글자 + 여백 크기를 너비,높이 Constraint로 생성하고 추가

            let widthConstraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: width)

            let heightConstraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height)

            addConstraint(widthConstraint)

            addConstraint(heightConstraint)

            mWidthConstraint = widthConstraint

            mHeightConstraint = heightConstraint

        }

        else {

            //두번째부터는 Constraint의 수치만 변경

            mWidthConstraint!.constant = width

            mHeightConstraint!.constant = height

        }

        

        //중앙 정렬

        textAlignment = .center

    }

}




핵심 원리는 그저 글자 전체크기를 불러와 여백을 더하여 NSLayoutConstraint를 만드는 것이다.

너무 간단해서 더이상 설명이 필요없다ㅎㅎ


 

 



수제 앱 장인: 고영진


(주)고영진모바일

1인기업 대표이사 겸 개발자

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 






사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2018.03.26 10:42

손쉽게 아이폰 앱을 새로 실행 시키는 코드 한줄



안드로이드에서는 메인 activity를 종료하고 다시 호출하면 바로 재시작이 된다.


하지만 아이폰에서는 그렇게 할수가 없었다. 혹시나 하는 마음에 재시작 시키는 함수를 찾아봤지만 이 또한 없었다. 나의 구글링 실력이 부족해서 못찾은거일수도 있지만... 좌절하고 있는 순간...


정말 우연하게 방법을 알아냈다. 사실 실수로 코드를 잘못 넣었는데... 재시작이 되는것이다... 아! 실수로 찍은 로또나 될것이지.. 어쨋든 기분은 좋다. 불로소득같은 느낌이다.



방법은 진짜 간단하다.

AppDelegate 의 applicationWillEnterForeground 에서 exit(0)를 호출해주면 된다.

아마도 잠에서 깨어나기 직전에 종료를 해주면 깨어나는 작업이 중단되지 않고 계속 진행되면서 재시작되는 효과가 나오는것 같다.


단점은 내가 원하는 시점이 아니라 앱이 깨어나는 순간에서만 적용된다는 것이다.

하지만 이는 아주 유용하게 쓰일수 있다.


iOS 특성상 앱을 완전 종료하지 않고 임시로 백그라운드로 내렸다가 다시 불려오는 경우가 많다.

(폰 아래 버튼은 종료 버튼이 아니라 홈 버튼이기 때문에)

즉, 앱이 완전 종료되고 다시 실행되어야만 하는 경우 매우 기특하게 사용될수 있다.



다음은 1시간 동안 앱이 포어그라운드로 깨어나지 않았다면 새로 실행 시키는 코드이다.

참고하여 다양하게 사용하길 바란다.



class AppDelegate: UIResponder, UIApplicationDelegate {


    let RESTART_TIME: TimeInterval = 3600

  

    var mResignTime: TimeInterval?


    func applicationWillResignActive(_ application: UIApplication) {

        mResignTime = NSDate().timeIntervalSince1970

    }


    func applicationWillEnterForeground(_ application: UIApplication) {

        if mResignTime != nil && NSDate().timeIntervalSince1970 - mResignTime! > RESTART_TIME {

            exit(0)

        }


    }

}



아이폰X와 아이폰7 (11.2)에서 테스트 해보았다. 잘 돌아간다. 굳!



 

 



수제 앱 장인: 고영진


(주)고영진모바일

1인기업 대표이사 겸 개발자

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 




사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2018.01.29 13:34

UIView의 원 테두리가 얇거나 작아보일때...



가끔 우리는 아주 사소한 차이로 고민할 때가 있다.


이거 왜이러니? 기분탓인가? 한번 살펴볼까? 귀찮다 이정도쯤이야 뭐..


하고 지나친적이 있지 않은가ㅎ 나는 많다ㅎㅎ

최근에도 그랬다.


UIView에서 StrokeEllipse 를 이용해 rect 크기 만큼 원 테두리를 그릴수 있다.

하지만 그려 놓고 나니 왠지 생각보다 작아보였고, 안에 색깔있는 원을 넣으니 테두리 굵기마저 얇아 보였다.. 뭐지???


처음엔 그냥 지나쳤다가 똥싸고 손안씻은것처럼 찝찝해서 한번 살펴보았다.

바보같았다... 왜 몰랐을까? 테두리 굵기는 양쪽으로 넓어진다는 사실을...

그래서 한쪽 굵기만큼 반이 얇아지고 작게 보이는 것이었다. 


아래와 같이 하면 금방 해결된다.. 혹시나 귀찮아서 그냥 지나칠 분들을 위해 글을 남긴다.




@IBInspectable var color: UIColor = UIColor.redColor()

@IBInspectable var borderColor: UIColor = UIColor.blackColor()

@IBInspectable var borderWidth: CGFloat = 2


override func drawRect(rect: CGRect) {

    let context = UIGraphicsGetCurrentContext()

        

    //테두리용 크기 얻기

    let inRect = strokeRect(rect, width: borderWidth)

        

    //색을 체운 그리기

    drawColor(context!, color: color, rect: inRect)

        

    // 테두리 그리기

    drawStroke(context!, width: borderWidth, color: borderColor, rect: inRect)

}

  

    

private func drawColor(context: CGContext, color: UIColor, rect: CGRect) {

    CGContextSetFillColorWithColor(context, color.CGColor)

    CGContextFillEllipseInRect(context, rect)

}

    

private func drawStroke(context: CGContext, width: CGFloat, color: UIColor, rect: CGRect) {

    CGContextSetLineWidth(context, width)

    CGContextSetStrokeColorWithColor(context, color.CGColor)

    CGContextStrokeEllipseInRect(context, rect)

}

    

private func strokeRect(rect: CGRect, width: CGFloat) -> CGRect {

    //굵기의 반만큼 rect 크기를 줄인다

    let width2 = width / 2

    return CGRectInset(rect, width2, width2)

}




 

 



수제 앱 장인: 고영진


(주)고영진모바일

1인기업 대표이사 겸 개발자

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 





사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2017.11.20 11:45

NSString을 마치 UILabel인 양 사용하기

UILabel을 추가하면 간단하게 글자를 넣을 수 있다.

하지만 sub view가 많아지면 어플이 무거워 질 수 있는데 이럴때에는 NSString을 써주면 좋다.


그렇다면 NSString를 UILabel처럼 배경색을 넣어서 마치 view인듯 사용해 보자~


 


먼저, NSString이 들어있는 클래스를 하나 만들자! NSString를 상속받아 확장해도 좋다. 나는 그냥 했다. TextBox라고 명명하겠다.


class TextBox {

    

    //정렬키 상수

    static let CENTER = 0

    static let LEFT = 8

    static let RIGHT = 16

    static let TOP = 1

    static let BOTTOM = 2

    

    var mText: NSString

    var mAlign: Int

    var mFontAttrDic: [String: NSObject]

    var mRect, mBgRect: CGRect

    var mPadding: (left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat)

    var mBgColor: UIColor

    var mCornerRadius: CGFloat

    

    init(text: NSString, align: Int, padding: (left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat), font: UIFont, color: UIColor, bgColor: UIColor, corner: CGFloat = 0) {

        mText = text

        mAlign = align  //정렬

        mFontAttrDic = [

            NSForegroundColorAttributeName: color,

            NSParagraphStyleAttributeName: NSMutableParagraphStyle(),

            NSFontAttributeName: font

        ]

        mRect = CGRect()    //글자 위치크기

        mBgRect = CGRect()  //배경 위치크기

        mPadding = padding  //글자와배경 간격

        mBgColor = bgColor  //배경색

        mCornerRadius = corner //둥근사각형 각크기

    }

    

    func setPoint(x: CGFloat, _ y: CGFloat) -> TextBox {

        //글자 원본크기

        let size = mText.sizeWithAttributes(mFontAttrDic)

        let width = size.width

        let height = size.height

        

        //크기 설정

        let padding = mPadding

        mRect.size.width = width

        mRect.size.height = height

        mBgRect.size.width = width + padding.left + padding.right

        mBgRect.size.height = height + padding.top + padding.bottom

        

        //위치 설정

        //가로 정렬

        switch mAlign >> 3 {

        case 1: //왼쪽 정렬

            mRect.origin.x = x + padding.left

            mBgRect.origin.x = x

            

        case 2: //오른쪽 정렬

            let x = x - padding.right - width

            mRect.origin.x = x

            mBgRect.origin.x = x - padding.left

            

        default:    //가운데 정렬

            let x = x - (width / 2)

            mRect.origin.x = x

            mBgRect.origin.x = x - padding.left

        }

        

        //세로 정렬

        switch mAlign & 7 {

        case 1: //위쪽 정렬

            mRect.origin.y = y + padding.top

            mBgRect.origin.y = y

            

        case 2: //아래쪽 정렬

            let y = y - padding.bottom - height

            mRect.origin.x = y

            mBgRect.origin.x = y - padding.top

            

        default:    //가운데 정렬

            let y = y - (height / 2)

            mRect.origin.y = y

            mBgRect.origin.y = y - padding.top

        }

        

        return self

    }

    

    func draw(context: CGContext) {

        //배경 출력

        CGContextSetFillColorWithColor(context, mBgColor.CGColor)

        

        if mCornerRadius == 0 {

            //사각형

            CGContextFillRect(context, mBgRect)

        }

        else {

            //둥근사각형

            CGContextAddPath(context, UIBezierPath(roundedRect: mBgRect, byRoundingCorners: .AllCorners, cornerRadii: CGSizeMake(mCornerRadius, mCornerRadius)).CGPath)

            CGContextFillPath(context)

        }

        

        //글자 출력

        mText.drawInRect(mRect, withAttributes: mFontAttrDic)

    }

}


위의 소스를 보면 알겠지만 미리 색이나 글자배경 간격등을 정해준 후 setPoint로 위치를 찍어주면 글자크기를 얻어 배경을 설정값에 맞게 만들어주고 draw로 그려준다.




자.. UIView를 확장하여 위의 TextBox를 이용해 보자


class TextBoxsView: UIView {


    var mTextBoxs: [TextBox]!

    

    func initTextBoxs() {

        //폰트

        let font = UIFont.systemFontOfSize(15)

        

        mTextBoxs = [TextBox]()

        

        //사각형 배경

        mTextBoxs.append(TextBox(text: "Rect", align: TextBox.CENTER, padding: (30, 50, 40, 10), font: font, color: UIColor.whiteColor(), bgColor: UIColor.blackColor()).setPoint(100, 200))

        

        //둥근사각형 배경

        mTextBoxs.append(TextBox(text: "RoundRect", align: TextBox.LEFT, padding: (30, 30, 30, 30), font: font, color: UIColor.greenColor(), bgColor: UIColor.blueColor(), corner: 10).setPoint(100, 400))

    }

    

    override func drawRect(rect: CGRect) {

        let context = UIGraphicsGetCurrentContext()

        

        //출력

        for textBox in mTextBoxs {

            textBox.draw(context!)

        }

    }

}


테스트를 위해 설정값이 틀린 사각형과 둥근사각형 둘을 만들었다. initTextBoxs를 호출하면 된다.




끝으로 ViewController에서 view를 위의 TextBoxsView로 바꾸고 호출한다.


class TextBoxViewController: ViewController {

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        (view as! TextBoxsView).initTextBoxs()

    }

}





결과♡





 

 



수제 앱 장인: 고영진


(주)고영진모바일

1인기업 대표이사 겸 개발자

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 




사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2017.05.30 16:08

UIButton 이미지를 손쉽게 정렬하자~

아이폰 Swift에서 UIButton 안의 이미지를 원하는 위치에 두려면 

Edge Inset을 이용하여 좌우상하에 여백을 줘야 한다


여간 귀찮고 불편한 일이 아닐 수 없다


그래서 좀 더 편하게 이미지를 옮기는 방법을 소개한다


원리는 무지 간단하다

Edge Inset를 가로,세로 기준으로 이미지 크기와 함께 각각 계산해 주면 된다


왼쪽 정렬 

(L: 왼쪽 여백, R: 오른쪽 여백, M: 여백, IW: 이미지 너비, BW: 버튼 너비)





가운데 정렬 





이 두가지 경우만 이용하면 나머지 정렬도 모두 가능하다


코딩하면 다음과 같다

 


class ImageButton: UIButton {

    

    //가로 정렬 기준 (-1: 왼쪽, 0: 가운데, 1: 오른쪽)

    @IBInspectable var horizontal: Int = 0

    

    //세로 정렬 기준 (-1: , 0: 가운데, 1: 아래)

    @IBInspectable var vertical: Int = 0

    

    //기준으로부터의 거리 (여백)

    @IBInspectable var point: CGPoint = CGPointMake(0, 0)

    

    //이미지 크기

    @IBInspectable var size: CGSize = CGSizeMake(0, 0)

    

    private var mSetSize: CGSize?

    

    override func drawRect(rect: CGRect) {

        let width = frame.width

        let height = frame.height

        

        //처음 또는 버튼 크기가 달라지면 다시 위치 계산

        if  mSetSize == nil || !(mSetSize?.width == width && mSetSize?.height == height) {

            locate()

            

            //버튼 크기값 저장

            mSetSize = CGSizeMake(width, height)

        }

    }

    

    //위치 계산후 적용

    private func locate() {

        //가로 정렬

        let width = size.width

        let x1 = point.x

        let x2 = frame.width - x1 - width

        switch (horizontal) {

        case -1: //왼쪽 정렬

            

            //이미지 왼쪽은 여백 그대로

            imageEdgeInsets.left = x1

            

            //이미지 오른쪽은 버튼 너비에서 margin, 이미지 너비를 뺀다

            imageEdgeInsets.right = x2

            

        case 1: //오른쪽 정렬


            //왼쪽 정렬과 반대로 한다

            //이미지 왼쪽은 버튼 너비에서 margin, 이미지 너비를 뺀다

            imageEdgeInsets.left = x2

            

            //이미지 오른쪽은 여백 그대로

            imageEdgeInsets.right = x1

            

        default: //가운데 정렬

            

            //이미지 왼쪽은 버튼의 가운데에서 여백 더하고 다시 이미지 너비의 절반을 뺀다

            imageEdgeInsets.left = ((frame.width / 2) + x1) - (width / 2)

            

            //이미지 오른쪽은 버튼 너비에서 위에서 계산한 왼쪽 좌표와 이미지 너비를 뺀다

            imageEdgeInsets.right = frame.width - (imageEdgeInsets.left + width)

        }

        

        //세로 정렬 (가로 정렬과 같은 원리로)

        let height = size.height

        let y1 = point.y

        let y2 = frame.height - y1 - height

        switch (vertical) {

        case -1: // 정렬

            

            imageEdgeInsets.top = y1

            imageEdgeInsets.bottom = y2

            

        case 1: //아랫 정렬

            

            imageEdgeInsets.top = y2

            imageEdgeInsets.bottom = y1

            

        default: //가운데 정렬

            

            imageEdgeInsets.top = ((frame.height / 2) + y1) - (height / 2)

            imageEdgeInsets.bottom = frame.height - (imageEdgeInsets.top + height)

        }

    }

}





스토리 보드로 적용해 보자






Custom Class를 위에서 만든 Image Button으로 바꾸고

다음과 같이 입력해 보자







실행하면 이런 모습이다 

버튼안의 이미지가 50x50 이고

가로는 왼쪽에서 20포인트, 세로는 가운데에서 10포인트 움직였다






 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 



사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2017.04.25 10:43

UIView에 동적으로 버튼을 넣을때 크기를 같게 맞출려면?

안드로이드에서는 layout_weight 로 쉽게 하위 뷰들의 크기를 맞출 수 있다


예를 들어 버튼을 넣는다면 버튼에 layout_weight=1 속성을 추가한 후 

LinearLayout에 넣기만 하면 된다

또한 버튼 사이 간격은 layout_margin 로 조절한다


그렇다면 iso의 swift에선 어떻게 하면 될까?

약간 손이 더 가긴 하지만 어렵지 않다





편하게 사용하기 위해 UIView를 커스텀해서 만들어 보았다

버튼 크기를 맞춰주는 동시에 사이 간격, 여백도 줄것이다




class ButtonsView: UIView {

    

    private var mGap: CGFloat!

    private var mPadding: (left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat)!

    

    var mDelegate: ButtonsViewDelegate?

    

    //버튼간의 공간과 여백을 설정한다

    func setPadding(gap: CGFloat?, left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat) {

        //버튼간의 공간

        mGap = gap

        

        //여백

        mPadding = (left, top, right, bottom)

    }

    

    //버튼을 추가한다

    func addButton(name: String, color: CGColor) {

        //버튼을 생성한다

        let view = UIButton()

        

        //Autoresize 끈다

        //왜냐하면 우리는 직접 Constraint 설정해주기 때문이다

        view.translatesAutoresizingMaskIntoConstraints = false

        view.setTitle(name, forState: .Normal)

        view.layer.backgroundColor = color

        view.layer.cornerRadius = 5

        

        let count = subviews.count

        let padding = mPadding

        

        if count == 0 {

            //첫번째 버튼은 부모뷰와 맞게하고 그저 여백만 준다

            

            //부모뷰에 버튼을 추가한다

            addSubview(view)

            

            //부모뷰와의 상위 여백을 만든다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: padding!.top))

            

            //부모뷰와의 하위 여백을 만든다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: -padding!.bottom))

            

            //부모뷰와의 왼쪽 여백을 만든다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1, constant: padding!.left))

            

            //부모뷰와의 오른쪽 여백을 만든다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1, constant: -padding!.right))

        }

        else {

            //두번째 버튼부터는 첫번째 버튼과 같은 크기로 하고, 사이를 설정해 값으로 띄운다

            

            //부모뷰에 버튼을 추가하기 전에 첫번째, 마지막 버튼과 마지막 constraint 가져온다

            let firstView = subviews.first

            let lastView = subviews.last

            let lastConstraint = constraints.last

            

            //부모뷰에 버튼을 추가한다

            addSubview(view)

            

            //첫번째 버튼과 같은 크기로 만든다

            //, 버튼들이 늘어난 버튼 갯수만큼 작아지면서 같아진다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: firstView, attribute: .Width, multiplier: 1, constant: 0))

            

            //첫번째 버튼과 상위를 맞춘다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: firstView, attribute: .Top, multiplier: 1, constant: 0))

            

            //첫번째 버튼과 하위를 맞춘다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal, toItem: firstView, attribute: .Bottom, multiplier: 1, constant: 0))

            

            //이전 버튼과 사이를 띄운다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: lastView, attribute: .Trailing, multiplier: 1, constant: mGap!))

            

            //마지막 constraint , 이전 버튼의 오른쪽 여백을 제거한다

            removeConstraint(lastConstraint!)

            

            //현재 버튼으로 다시 오른쪽 여백을 만든다

            addConstraint(NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1, constant: -padding!.right))

        }

        

        //클릭 이벤트를 잡는다

        view.addTarget(self, action: "clickDetected:", forControlEvents: .TouchUpInside)

    }

    

    //모든 버튼을 제거한다

    func removeAllButtons() {

        for view in subviews {

            view.removeFromSuperview()

        }

    }

    

    //클릭 이벤트를 보낸다

    func clickDetected(sender: UIButton) {

        if mDelegate != nil {

            //클릭 이벤트의 전달 변수로 버튼 객체와 버튼 인덱스 번호를 보낸다

            mDelegate?.clickButtonsDetected(sender, position: subviews.indexOf(sender)!)

        }

    }

}


//클릭 이벤트를 보내기 위한 델리게이트

protocol ButtonsViewDelegate {

    func clickButtonsDetected(sender: UIButton, position: Int)

}







보통 이런 방식은 탭으로 많이 사용된다

탭 버튼으로 적용해 보자


먼저 스토리보드에서 화면을 다음과 같이 구성한다.

위의 푸른색 Add 와 clear 버튼은 각각 탭버튼 추가가 탭버튼 모두 삭제 기능을 갖는다

중간의 검정색 Name 과 Position 라벨은 탭버튼 이름과 번호를 표시한다.

끝으로 가장 아래의 회색 공간은 위에서 만든 buttonsView이다. 이곳에 탭버튼이 나타날것이다 









뷰컨트롤러이다

위에서 만든 프로토콜 델리게이션을 추가하고 처음에 탭버튼 3개를 만들어 놓는다

추가, 제거 버튼 및 탭버튼 클릭 함수도 등록한다




class ViewController: UIViewController, ButtonsViewDelegate {


    @IBOutlet var tabView: ButtonsView!

    @IBOutlet var buttonLabel: UILabel!

    @IBOutlet var positionLabel: UILabel!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        //delegate 설정

        tabView.mDelegate = self

        

        //버튼 설정 생성

        tabView.setPadding(10, left: 10, top: 10, right: 10, bottom: 10)

        tabView.addButton("버튼1", color: UIColor.blueColor().CGColor)

        tabView.addButton("버튼2", color: UIColor.redColor().CGColor)

        tabView.addButton("버튼3", color: UIColor.greenColor().CGColor)

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        

    }


    @IBAction func clickAddDetected(sender: AnyObject) {

        //버튼 추가

        tabView.addButton("new", color: UIColor.brownColor().CGColor)

    }

    

    @IBAction func clickClearDetected(sender: AnyObject) {

        //모든 버튼 제거

        tabView.removeAllButtons()

    }

    

    func clickButtonsDetected(sender: UIButton, position: Int) {

        //버튼 출력

        buttonLabel.text = sender.titleLabel?.text

        positionLabel.text = "p: \(position)"

    }

}






실행해 보자







 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 



사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2017.03.21 19:18

기준점을 중앙으로 Scale UIView 애니메이션 하기

swift 에서는 간단하게 뷰 애니메이션을 구현할 수 있다.


그런데 크기를 변경시키는 애니메이션에서는 주의 해야 할 점이 있다

바로 기준점이다



다음을 보자



let width = frame.width

let height = frame.height

        

frame.size.width = width * 0.2

frame.size.height = height * 0.2

        

UIView.animateWithDuration(1, delay: 0, options: [.CurveEaseInOut], animations: {

      self.frame.size.width = width

      self.frame.size.height = height

            

}, completion: nil)



20% 작은 크기에서 원래 크기로 애니메이션 효과를 주었다




기준점이 0이 되어 버렸다. 당연한 결과이다.

하지만 보통 중앙을 기준으로 할때가 많지 않은가?


이렇게 바꿔보자



let x = frame.origin.x

let y = frame.origin.y

let width = frame.width

let height = frame.height

        

frame.origin.x = x + ((width * 0.8) / 2)

frame.origin.y = y + ((height * 0.8) / 2)

frame.size.width = width * 0.2

frame.size.height = height * 0.2

        

UIView.animateWithDuration(1, delay: 0, options: [.CurveEaseInOut], animations: {

     self.frame.origin.x = x

     self.frame.origin.y = y

     self.frame.size.width = width

     self.frame.size.height = height

            

}, completion: nil)



X,Y 좌표를 100 - 20 = 80%의 반으로 옮긴 후 원래 위치로 애니메이션을 하였다




기준점이 중앙이 되었다. 

이런 방식으로 위치도 같이 변경해 주면 다양한 기준점으로 애니메이션 해줄 수 있다.



 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 




사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2017.02.22 17:31

이미지를 회전시키고 재사용하는 꿀팁



프로그램 안에서 같은 이미지를 


여러곳에 사용할때! 

그것도 회전시켜 사용할때!


원본 이미지의 회전된 이미지를 테이블안에 넣어 사용하면 간편하고 빠르다.

물론 메모리를 좀 차지하고, 

요즘 디바이스도 워낙 좋아져 속도도 별반 차이 없겠지만


그래도 엄청 많은 곳에 엄청 빈번하게 회전 이미지를 사용할때는 유용하다ㅋ




class RotateImageTable {

    

    let mSrcImage: UIImage

    let mUnit: CGFloat

    

    var mImages: [Int: UIImage]

    

    init(srcName: String, unit: CGFloat) {

        mSrcImage = UIImage(named: srcName)!    //원본 이미지

        mImages = [Int: UIImage]()  //이미지 테이블

        mUnit = unit    //회전시킬 라디안 단위

    }

    

    private func rotate(radian: CGFloat) -> UIImage {

        //이미지 크기

        let size = mSrcImage.size

        let midX = size.width / 2

        let midY = size.height / 2

        

        //회전 작업 시작

        UIGraphicsBeginImageContext(size)

        

        //회전축을 변경 회전시킴

        let context = UIGraphicsGetCurrentContext()

        CGContextTranslateCTM(context, midX, midY)

        CGContextRotateCTM(context, radian)

        CGContextTranslateCTM(context, -midX, -midY)

        

        //이미지에 적용

        CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), mSrcImage.CGImage)

        let image = UIGraphicsGetImageFromCurrentImageContext()

        

        //회전 작업

        UIGraphicsEndImageContext()


        return image

    }

    

    func image(radian: CGFloat) -> UIImage {

        //라디안 각도를 단위에 맞게 변경

        let key = Int(round(radian / mUnit))

        if key == 0 {

            //각도가 0이라면 원본 이미지 사용

            return mSrcImage

        }

        

        let image = mImages[key]

        if image == nil {

            //테이블에 해당 각도가 없다면 회전 이미지 생성

            let rotateImage = rotate(radian)

            mImages[key] = rotateImage

            return rotateImage

        }

       

        //테이블에 저장된 회전 이미지 사용

        return image!

    }

}





요렇게 사용한다



//0.02 라디안씩 회전하는 이미지 테이블 생성

let imageTable = RotateImageTable(srcName: "arrow.png", unit: 0.02)

        

//이미지 사용

imageTable.image(1.4).drawInRect(CGRectMake(50, 50, 150, 150))

imageTable.image(-0.67).drawInRect(CGRectMake(30, 200, 400, 400))

        

addSubview(UIImageView(image: imageTable.image(2.1)))

addSubview(UIImageView(image: imageTable.image(-3)))




 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 




사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/ios 2016.12.19 10:45

쿼츠2D로 자유롭게 폰트 크기를 바꿔보자



직접  UIView를 상속해서 자신만의 UI객체를 만들때 쿼츠 2D를 사용한다

이때, 폰트 크기는 어떻게 바꿀수 있을까?


NSString를 이용하면 간단하다.



class MyView: UIView {

    

    var mFontAttrDic: [String: NSObject]!

    var mFont: UIFont!

    var mText: NSString!

    

    override func drawRect(rect: CGRect) {

        //문자 출력

        mText.drawInRect(CGRectMake(0, 0, 100, 100), withAttributes: mFontAttrDic)

    }

    

    func intiFont() {

        //NSString 설정

        mText = "테스트"

        

        //폰트 설정

        mFont = UIFont(name: "SeoulNamsan CM", size: 10)!

        

        //NSString attribute 배열 설정

        mFontAttrDic = [

            NSForegroundColorAttributeName: UIColor.blackColor(),

            NSParagraphStyleAttributeName: NSMutableParagraphStyle(),

            NSFontAttributeName: mFont

        ]

    }

    

    func changeFontSize(size: CGFloat) {

        //폰트 사이즈를 변경하여 arriribute 배열 적용

        mFontAttrDic[NSFontAttributeName] = mFont.fontWithSize(size)

        

        //화면 갱신

        setNeedsDisplay()

    }

}




사용해 보자


let myView = MyView(frame: frame)

myView.initFont()


//폰트 크기를 바꾸고 싶을때 호출

myView.changeFontSize(20)





 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 



사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
TOTAL 69,579 TODAY 8