Blog

  • How to animate path using CoreAnimation

    How to animate path using CoreAnimation

    I wrote a Behind the scene of delightful animation it is about Animation patterns in modern iOS Apps. In this post, I’ll introduce how to animate path using CoreAnimation.

    Before we start coding, we need to prepare the vector image like an SVG and then convert it to UIBeizierpath.

     

    SVG Image

    SVG acronym is Scalable Vector Graphics developed by W3C. Unfortunately, iOS does not support the SVG format. So We need to convert it to UIBeizierpath.

    #block-yui_3_17_2_1_1589705258661_10561 .sqs-gallery-block-grid .sqs-gallery-design-grid { margin-right: -0px; }
    #block-yui_3_17_2_1_1589705258661_10561 .sqs-gallery-block-grid .sqs-gallery-design-grid-slide .margin-wrapper { margin-right: 0px; margin-bottom: 0px; }

    Look at path tag. Each character has the meaning of a drawing command. Uppercase uses absolute position, and lowercase uses relative position.

    • M = moveto

    • L = lineto

    • H = horizontal lineto

    • V = vertical lineto

    • C = curveto

    • S = smooth curveto

    • Q = quadratic Bézier curve

    • T = smooth quadratic Bézier curveto

    • A = elliptical Arc

    • Z = closepath

    We can convert it to UIBeizierPath.

    • move(to: CGPoint)

    • addCurve(to: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)

    • addQuadCurve(to: CGPoint, controlPoint: CGPoint)

    • addLine(to: CGPoint)

    • addArc(withCenter: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockWise: Bool)

     

    How to convert SVG to UIBeizierPath?

    Here is an example of Swift Logo.

    carbonDrawing.JPG

    Let’s convert it to UIBeizierPath step by step.

    <path d="m29.885 33.047c-4.667 2.696-11.084 2.973-17.54 0.206-5.2273-2.224-9.5646-6.117-12.345-10.565 1.3346 1.112 2.8916 2.002 4.5598 2.78 6.6672 3.125 13.333 2.911 18.024 0.008-0.003-0.003-0.005-0.005-0.007-0.008-6.673-5.116-12.345-11.789-16.571-17.238-0.8901-0.8898-1.5574-2.002-2.2247-3.0029 5.1159 4.671 13.235 10.565 16.126 12.234-6.116-6.451-11.566-14.458-11.344-14.236 9.676 9.787 18.685 15.348 18.685 15.348 0.298 0.168 0.528 0.308 0.713 0.433 0.195-0.496 0.366-1.011 0.51-1.545 1.557-5.672-0.222-12.123-4.115-17.461 9.008 5.4495 14.347 15.681 12.122 24.245-0.058 0.231-0.121 0.459-0.189 0.683 0.026 0.031 0.052 0.063 0.078 0.096 4.448 5.561 3.225 11.455 2.669 10.343-2.413-4.722-6.88-3.278-9.151-2.32z"/> 

    StartPoint is 29.88, 33.05

    StartPoint is 29.88, 33.05

    //First drawing command
    //m29.885 33.047
    move(to: CGPoint(x: 29.885, y: 33.05)

    The m command is easy. Just move the point using move(to: CGPoint).

    Second point is 12.34, 33.25. It is calculated by startPoint x: 29.885 - 17.54 = 12.35 and startPoint: y: 33.05 + 0.206 = 33.25.

    Second point is 12.34, 33.25. It is calculated by startPoint x: 29.885 – 17.54 = 12.35 and startPoint: y: 33.05 + 0.206 = 33.25.

    //Second drawing command
    //c-4.667 2.696-11.084 2.973-17.54 0.206
    addCurve(
    to: CGPoint(x: 12.35, y: 33.25), 
    controlPoint1: CGPoint(x: 25.22, y: 35.74), 
    controlPoint2: CGPoint(x: 18.8, y: 36.02)
    )

    Look at the c character. The c is meaning that curveTo is a relative position. It takes 3 points, which are controlPoint1, controlPoint2, and currentPoint. I was very confused that how can it be converted from (-4.667, 2.696), (-11.084, 2.973), and (-17.54, 0.206) to (12.35, 33.25), (25.22, 35.74), and (18.8, 36.02).

    Let’s look again. We take 3 points, which are controlPoint1, controlPoint2, and current Position.

    Let’s look again. We take 3 points, which are controlPoint1, controlPoint2, and current Position.

    controlPoint2.JPG

    controlPoint1.JPG

    The startPosition is CGPoint(x: 29.885, y: 33.05). And It was added curveTo relative to startPosition.

    • CurrentPosition(x: 12.35, y: 33.25) is calculated by 29.885 – 17.54 = 12.35 and 33.05 + 0.206 = 33.25

    • ControlPoint1(x: 25.22, y: 35.74) is calculated by 29.885 – 4.667 = 25.22 and 33.05 + 2.697 = 35.74

    • ControlPoint2(x: 18.8, y: 36.02) is calculated by 29.885 – 11.084 = 18.8 and 33.05 + 2.973 = 36.02

    And next position is also same. Just take a 3 position and calculate it relative to currentPosition(x: 12.35, y: 33.25) which is updated after add curveTo position. Here is the full path of Swift Logo.

    extension UIBezierPath {
    static var swift: UIBezierPath = {
        let bezierPath = UIBezierPath()
        bezierPath.move(to: CGPoint(x: 29.88, y: 33.05))
        bezierPath.addCurve(to: CGPoint(x: 12.35, y: 33.25), controlPoint1: CGPoint(x: 25.22, y: 35.74), controlPoint2: CGPoint(x: 18.8, y: 36.02))
        bezierPath.addCurve(to: CGPoint(x: 0, y: 22.69), controlPoint1: CGPoint(x: 7.12, y: 31.03), controlPoint2: CGPoint(x: 2.78, y: 27.14))
        bezierPath.addCurve(to: CGPoint(x: 4.56, y: 25.47), controlPoint1: CGPoint(x: 1.33, y: 23.8), controlPoint2: CGPoint(x: 2.89, y: 24.69))
        bezierPath.addCurve(to: CGPoint(x: 22.58, y: 25.48), controlPoint1: CGPoint(x: 11.23, y: 28.59), controlPoint2: CGPoint(x: 17.89, y: 28.38))
        bezierPath.addCurve(to: CGPoint(x: 22.58, y: 25.47), controlPoint1: CGPoint(x: 22.58, y: 25.47), controlPoint2: CGPoint(x: 22.58, y: 25.47))
        bezierPath.addCurve(to: CGPoint(x: 6.01, y: 8.23), controlPoint1: CGPoint(x: 15.9, y: 20.35), controlPoint2: CGPoint(x: 10.23, y: 13.68))
        bezierPath.addCurve(to: CGPoint(x: 3.78, y: 5.23), controlPoint1: CGPoint(x: 5.12, y: 7.34), controlPoint2: CGPoint(x: 4.45, y: 6.23))
        bezierPath.addCurve(to: CGPoint(x: 19.91, y: 17.46), controlPoint1: CGPoint(x: 8.9, y: 9.9), controlPoint2: CGPoint(x: 17.02, y: 15.79))
        bezierPath.addCurve(to: CGPoint(x: 8.56, y: 3.23), controlPoint1: CGPoint(x: 13.79, y: 11.01), controlPoint2: CGPoint(x: 8.34, y: 3))
        bezierPath.addCurve(to: CGPoint(x: 27.25, y: 18.57), controlPoint1: CGPoint(x: 18.24, y: 13.01), controlPoint2: CGPoint(x: 27.25, y: 18.57))
        bezierPath.addCurve(to: CGPoint(x: 27.96, y: 19.01), controlPoint1: CGPoint(x: 27.55, y: 18.74), controlPoint2: CGPoint(x: 27.78, y: 18.88))
        bezierPath.addCurve(to: CGPoint(x: 28.47, y: 17.46), controlPoint1: CGPoint(x: 28.16, y: 18.51), controlPoint2: CGPoint(x: 28.33, y: 18))
        bezierPath.addCurve(to: CGPoint(x: 24.36, y: 0), controlPoint1: CGPoint(x: 30.03, y: 11.79), controlPoint2: CGPoint(x: 28.25, y: 5.34))
        bezierPath.addCurve(to: CGPoint(x: 36.48, y: 24.25), controlPoint1: CGPoint(x: 33.36, y: 5.45), controlPoint2: CGPoint(x: 38.7, y: 15.68))
        bezierPath.addCurve(to: CGPoint(x: 36.29, y: 24.93), controlPoint1: CGPoint(x: 36.42, y: 24.48), controlPoint2: CGPoint(x: 36.36, y: 24.7))
        bezierPath.addCurve(to: CGPoint(x: 36.37, y: 25.02), controlPoint1: CGPoint(x: 36.32, y: 24.96), controlPoint2: CGPoint(x: 36.34, y: 24.99))
        bezierPath.addCurve(to: CGPoint(x: 39.04, y: 35.37), controlPoint1: CGPoint(x: 40.82, y: 30.59), controlPoint2: CGPoint(x: 39.59, y: 36.48))
        bezierPath.addCurve(to: CGPoint(x: 29.88, y: 33.05), controlPoint1: CGPoint(x: 36.62, y: 30.65), controlPoint2: CGPoint(x: 32.16, y: 32.09))
        bezierPath.close()
        return bezierPath
    }()
    }

     

    Let’s animating it.

    Path Animation

    PathAnimation

    PathAnimation

    class ViewController: UIViewController {
        var swiftPath: UIBezierPath = .swift
        lazy var logoLayer: CAShapeLayer = {
           let logoLayer = CAShapeLayer()
            logoLayer.path = swiftPath.cgPath
            logoLayer.strokeEnd = 0
            logoLayer.strokeStart = 0
            logoLayer.lineWidth = 2
    
            logoLayer.borderColor = UIColor.black.cgColor
            logoLayer.strokeColor = UIColor.black.cgColor
            logoLayer.fillColor = UIColor.white.cgColor
    
            logoLayer.position = CGPoint(x: 161, y: 247)
            return logoLayer
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.layer.addSublayer(logoLayer)
            startPathAnimation()
        }
    
        func startPathAnimation() {
            let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
            pathAnimation.fromValue = 0
            pathAnimation.toValue = 1
            pathAnimation.duration = 2
            pathAnimation.isRemovedOnCompletion = false
            pathAnimation.fillMode = .forwards
            logoLayer.add(pathAnimation, forKey: "line")
        }
    }

     

    Fill Color Animation

    fillColor.gif

    func startFillColorAnimation() {
      let fillColorAnimation: CABasicAnimation = CABasicAnimation(
      keyPath: "fillColor"
      )
      fillColorAnimation.duration = 1
      //Start Fill Color Animation after finishing path animation.
      fillColorAnimation.beginTime = CACurrentMediaTime() + 2
      fillColorAnimation.fromValue = UIColor.clear.cgColor
      fillColorAnimation.toValue = UIColor.red.cgColor
      fillColorAnimation.fillMode = .forwards
      //Keep color after complete animation
      fillColorAnimation.isRemovedOnCompletion = false
      logoLayer.add(fillColorAnimation, forKey: "fill")
     }

     

    Fill Gradient Color Animation

    Unlike SVG, CAShapeLayer doen’t support gradient color. So We should use CAGradientLayer to fill the gradient color in Swift logo. I’ll show you how to change the gradient color from original orange gradient color of Swift logo to blue gradient color of SwiftUI logo.

    let swiftUIColor: [CGColor] = [
      UIColor(red: 0/255, green: 240/255, blue: 245/255, alpha: 1).cgColor,
      UIColor(red: 0/255, green: 5/255, blue: 140/255, alpha: 1).cgColor
    ]
    
    let swiftColor: [CGColor] = [
    UIColor(red: 248/255, green: 138/255, blue: 54/255, alpha: 1).cgColor,
    UIColor(red: 253/255, green: 32/255, blue: 32/255, alpha: 1).cgColor
    ]
    override func viewDidLoad() {
        super.viewDidLoad()
        view.layer.addSublayer(logoLayer)
    
        startPathAnimation()
        startFillColorAnimation()
        fillGradientColor(colors: swiftColor)
    }
    
    func fillGradientColor(colors: [CGColor]) {
      let gradient = CAGradientLayer(layer: logoLayer)
      gradient.frame = swiftPath.bounds
      gradient.colors = colors
      logoLayer.addSublayer(gradient)
    }

    🤪 CAGradientLayer cover the CAShapeLayer.

    🤪 CAGradientLayer cover the CAShapeLayer.

    To fix CAGradientLayer cover the Swift logo, We need to clip mask before addSublayer.

    func fillGradientColor(colors: [CGColor]) {
      let gradient = CAGradientLayer(layer: logoLayer)
      gradient.frame = swiftPath.bounds
      gradient.colors = colors
      //Shape Mask!
      let shapeMask = CAShapeLayer()
      shapeMask.path = swiftPath.cgPath
      gradient.mask = shapeMask
      logoLayer.addSublayer(gradient)
    }

    Tada~ 😎 Looks great!

    Tada~ 😎 Looks great!

    Ok. Almost done! Let’s fill gradient color.

    override func viewDidLoad() {
      super.viewDidLoad()
      view.layer.addSublayer(logoLayer)
    
      startPathAnimation()
      startFillColorAnimation()
      fillGradientColor(colors: [UIColor.clear.cgColor, UIColor.clear.cgColor])
    }
    
    func startPathAnimation() {
      let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
      pathAnimation.fromValue = 0
      pathAnimation.toValue = 1
      pathAnimation.duration = 2
      pathAnimation.isRemovedOnCompletion = false
      pathAnimation.fillMode = .forwards
      logoLayer.add(pathAnimation, forKey: "line")
    }
    
    func startFillColorAnimation() {
      let fillColorAnimation: CABasicAnimation = CABasicAnimation(keyPath: "fillColor")
      fillColorAnimation.duration = 1
      fillColorAnimation.beginTime = CACurrentMediaTime() + 2
      fillColorAnimation.fromValue = UIColor.clear.cgColor
      fillColorAnimation.toValue = UIColor.red.cgColor
      fillColorAnimation.fillMode = .forwards
      fillColorAnimation.isRemovedOnCompletion = false
      logoLayer.add(fillColorAnimation, forKey: "fill")
    }
    
    func fillGradientColor(colors: [CGColor]) {
      let gradient = CAGradientLayer(layer: logoLayer)
      gradient.frame = swiftPath.bounds
      gradient.colors = colors
      //Shape Mask!
      let shapeMask = CAShapeLayer()
      shapeMask.path = swiftPath.cgPath
      gradient.mask = shapeMask
      logoLayer.addSublayer(gradient)
    
      //Start Animation
      startGradientAnimation(gradientLayer: gradient)
    }
    
    func startGradientAnimation(gradientLayer: CAGradientLayer) {
      let colorAnimation : CABasicAnimation = CABasicAnimation(keyPath: "colors")
      colorAnimation.duration = 2
      colorAnimation.beginTime = CACurrentMediaTime() + 3
      colorAnimation.fromValue = swiftColor
      colorAnimation.toValue = swiftUIColor
      colorAnimation.isRemovedOnCompletion = false
      colorAnimation.fillMode = .forwards
      gradientLayer.add(colorAnimation, forKey: "colors")
    }

    All done!

    All done!

    Thank you for reading my article. If you find any awkward expressions, I would appreciate your advice.

     

    I want to say that Thank you for review this post!

    Divjjot Singh helps me to improve my English writing skills. Thank you!

  • 서울 문정동 골목 떡볶이

    서울 문정동 골목 떡볶이

    서울 송파구 문정동 로데오거리에 위치한 떡볶이 맛집을 소개합니다.

    일단 이 집은 매운 맛의 떡볶이는 전혀 아니고 약간 단맛이 아주 살짝 느껴지는 양념의 떡볶이 집입니다. 떡은 밀가루 떡이고 저는 떡이 정말 맛있게 느껴졌습니다. 동네 근처에 있어서 자주 가던 떡볶이 집인데 방송에 여러번 소개된 이후로 사람이 조금씩 많아졌어요.

    문정동 골목 떡볶이


    문정동 골목떡볶이
    문정동 로데오거리의 주택가 골목에 위치
    문정동 골목 떡볶이 영업시간
    영업시간입니다. 주말은 오후 1시부터 오픈!
    문정동 골목 떡볶이 대기 줄
    방송에도 여러 번 소개되어서 항상 사람들이 많습니다. 그래도 대기시간은 한 10분도 안걸려요.
    떡볶이 메뉴
    밀떡 떡볶이
    매운 맛은 없고 약간의 단 맛의 느껴지고 어묵의 향을 느낄 수 있는 맛입니다.

    위치


    워드프레스의 기본 지도인 OpenStreetMap이 한국 지형을 아직 제대로 지원 안해주는 것 같네요. 주소는 서울특별시 송파구 문정로1길 33 (문정동)입니다.

    주차


    가게 근처는 주택 골목이라서 주차할 수 없고 걸어서 1분거리에 문정동 로데오 거리 길가에 유료로 주차를 할 수 있습니다. 주차비는 한 시간에 1,000원 정도 했던 것 같아요.

  • 뉴욕 엠파이어 스테이트 빌딩 전망대

    뉴욕 엠파이어 스테이트 빌딩 전망대

    뉴욕은 총 3번을 방문했지만 가장 기억에 남는 걸 묻는다면 전 주저없이 엠파이어 스테이트 빌딩(Empire State Building)에 올라가서 바라본 뉴욕 시내의 야경을 말할 것입니다.

    이 빌딩은 뉴욕에서 가장 높은 건물은 아니지만 뉴욕의 중심부에 자리잡고 있어서 전망대에서 바라보는 뉴욕 시내의 뷰가 정말 아름답습니다. 1931년도에 완공된 건물이고 2012년도까지는 뉴욕에서 가장 높은 건물이었습니다.

    제가 이 빌딩의 전망대를 추천하는 또 다른 이유는 뉴욕 시내에서 가장 높은 실외 전망대이기 때문이죠. 유리넘어 바라보는 전망대와는 완전히 다른 느낌입니다. 전망대는 86층의 실외 전망대와 102층의 실내 전망대가 있으며 개인적으로 86층 실외 전망대를 추천합니다.

    86층 전망대에서 바라본 뉴욕 시내


    엠파이어 스테이트 빌딩
    눈으로 본 멋진 광경을 사진에 다 담을 수가 없었습니다. 😭
    엠파이어 스테이트 빌딩

    위치


    Empire State Building의 위치입니다. 지도에서 2 블록 거리에 Korea Town이 있습니다. 코리아 타운에서 저녁을 먹고 전망대 올라가 보시는 것도 좋을 것 같아요.

  • 워싱턴 DC 자연사 박물관, 항공 우주 박물관 투어

    워싱턴 DC 자연사 박물관, 항공 우주 박물관 투어

    워싱턴 DC는 무료 입장할 수 있는 박물관이 많이 있습니다. 일정을 잘 조율하면 2박 3일동안 왠만한 박물관은 다 돌아볼 수 있죠. 저는 자연사 박물관항공우주 박물관을 다녀왔습니다.

    항공 우주 박물관


    National Air and Space Museum은 항공의 역사, 미국과 러시아의 경쟁등을 한 눈에 볼 수 있는 박물관입니다. 달 착륙과 관련된 자료들도 볼 수 있고 실제 우주에 다녀왔던 우주선과 우주복 등이 전시되어 있어요.

    항공우주박물관
    항공우주박물관
    항공우주박물관
    놀이기구를 탑승할 수 있는 공간
    항공우주박물관
    소형 놀이기구에서 바라본 항공모함
    항공우주박물관
    항공우주박물관

    자연사 박물관(National Museum of Natural History)


    미국 전역에 정말 많은 자연사 박물관이 있고 이미 다른 지역에서 자연사 박물관을 다녀왔지만 한 가지 보고 싶은 보물(?)이 있다고해서 다녀왔습니다. 바로 호프 다이아몬드죠. 박물관 홈페이지에서 오픈 시간을 확인하고 일정 세워보세요.

    호프 다이아몬드
    저주받은 다이아몬드라고 불리는 Hope Diamond
    호프 다이아몬드
    45.5 캐럿이라고 합니다

    호프 다이아몬드는 자연사 박물관 2층에 위치하고 있습니다. 명성 때문에 그런지는 몰라도 항상 사람들이 몰려있어요.

    위치


    위의 지도를 보시면 자연사 박물관과 항공우주 박물관 외에도 근처에 많은 박물관이 있죠. 걸어갈 수 있을 정도로 가까운 거리입니다. 그리고 지도 맨 오른쪽에 자유의 여신상을 디자인한 Frédéric Auguste Bartholdi의 분수가 있습니다. 가까운 거리에 있으니 혹시 관심 있으신 분은 들려보세요.

    박물관 근처에 가볼만 한 곳은? Botanic Garden


    1978년도에 만든 Frédéric Auguste Bartholdi의 작품
    분수를 만드신 분은 12년 뒤인 1890년에 자유의 여신상을 만드셨어요.

    이상으로 워싱턴 DC 박물관 투어를 마치겠습니다. 😃

  • 애플 덕후라면 가볼만한 Apple Store 워싱턴 DC 카네기 라이브러리

    애플 덕후라면 가볼만한 Apple Store 워싱턴 DC 카네기 라이브러리

    워싱턴 DC에서 꼭 가보고 싶었던 애플 스토어가 있었습니다. 바로 카네기 라이브러리 건물을 임대해서 애플 스토어로 꾸민 곳이었죠.

    역사


    위키피디아에 따르면 워싱턴 DC 최초의 카네기 도서관이자 공공 도서관이었으며 1903년도에 생긴 것으로 추정됩니다. 1969년에는 국립 유적지에 공공 도서관으로 등록되었고 2016년부터 애플의 제안으로 애플 스토어로 운영하기 시작했습니다. 현재 건물 이름은 Apple Carnegie Library로 변경되었습니다.

    카네기 도서관
    건물 외벽에 빛나는 애플로고
    애플 스토어
    애플 스토어

    매장안에서 찍은 사진은 아쉽게도 없네요. 저는 여기서 애플 스마트 배터리를 구입했어요. ㅎㅎ 내부 사진을 보고 싶으신 분들을 위해 애플 스토어 링크 첨부했어요.

    위치


    애플 덕후라면 워싱턴 DC의 카네기 라이브러리를 방문해보세요~! 👍

  • How to display the temperature with unit?

    How to display the temperature with unit?

    In this post, I’ll share how to display the temperature with unit using the MeasurementFormatter.

    MeasurementFormatter

    let numFormatter = NumberFormatter()
    numFormatter.maximumFractionDigits = 0
    
    let measureFormatter = MeasurementFormatter()
    measureFormatter.numberFormatter = numFormatter
    
    let kelvin: Double = 294
    var temperature: String = ""
    
    let kelvinTemperature = Measurement(
        value: kelvin,
        unit: UnitTemperature.kelvin
    )
    
    //temperature is automatically changed the celcius / farenheit depending on locale.
    temperature = measureFormatter.string(from: kelvinTemperature)
    
    //70°F
    print(temperature)

     

    UnitStyle

    //70 degrees Fahrenheit
    measureFormatter.unitStyle = .long
    //70°F
    measureFormatter.unitStyle = .medium
    //70°
    measureFormatter.unitStyle = .short

    The default unitStyle is medium. You can set the three different unitStyle.

    weather.PNG

    Apple Weather App

    The unitStyle is short.

  • 워싱턴 DC 조지타운의 이탈리안 음식점 Filomena Ristorante

    워싱턴 DC 조지타운의 이탈리안 음식점 Filomena Ristorante

    워싱턴 DC Georgetown에 위치한 이탈리안 레스토랑 Filomena Ristorante을 소개합니다. 이 음식점은 공식 사이트에 따르면 1980년에 뉴욕에서 워싱턴으로 옮기고 오픈했다고 합니다.

    메뉴


    가게 안으로 들어가서 계단 아래로 내려가면 레스토랑이 있습니다. 가게 내부가 어두운 편이라서 사진을 찍었는데 다 어둡게 나왔네요 😅

    이탈리안 레스토랑
    식전 빵
    파스타
    해산물 리조또와 파스타

    메뉴가 정말 많아서 주문할 때 좀 어려웠습니다. 저는 총 4개의 메뉴를 시켜서 5명이서 먹었는 데 양이 엄청 많아서 충분했어요.

    제가 시켰던 메뉴

    • Misto Di Mare Alla Filomena
      • 홍합과 파스타 그리고 새우가 들어있는 파스타
    • Cannelloni Frutti Di Mare
      • 새우, 게살, 가리비가 들어있는 리조또

    나머지 2개는 기억이 나지 않습니다 😥 메뉴 사진들을 더 보고 싶으시다면 이 레스토랑의 공식 인스타 계정에 들어가보세요.

    치즈케익

    디저트 메뉴도 있었는데 치즈케익이 유명한 것 같았어요.

    위치


    위치는 정말 찾아가기 쉬운 곳에 있습니다. 조지타운에서 워터프론트 공원으로 내려가는 길가에 위치하고 있어요. 워터 프론트공원을 들렸다가 저녁 먹으러 가는 것을 추천드립니다.

    예약


    반드시 예약하고 가시길 추천드립니다. 저는 예약 안하고 갔다가 거의 50분넘게 기다렸어요. 예약은 레스토랑 사이트에서 하실 수 있습니다.

  • 뉴욕 공립도서관 (New York Public Library)

    뉴욕 공립도서관 (New York Public Library)

    뉴욕 한복판에 위치한 공립도서관을 방문했습니다. 입장은 무료이고 열람실도 어마하게 커서 자리도 넉넉했어요. 뉴욕에 오랫동안 머무를 일이 있다면 여유롭게 맥북을 들고가서 개발하고 싶다는 생각을 잠깐 했습니다.

    뉴욕 도서관
    라이온킹에 나오는 사자랑 비슷하게 생긴 사자상

    역사


    뉴욕공립도서관의 소개글을 통해 역사를 간략하게 설명드리면 1895년에 New York Public Library를 설립하였고 그 후로 공사를 시작해서 1911년에 현재의 건물이 완공되어 오픈했다고 합니다. 100년이 넘은 건물이죠. 그럼 내부로 들어가볼까요?

    공립도서관 내부

    뉴욕 도서관
    뉴욕 도서관
    해리포터에 나오는 도서관 같은 느낌. 자리마다 콘센트가 있어서 노트북으로 공부하는 사람들도 많았습니다.
    뉴욕 도서관

    위치


    지도를 보시면 도서관은 브라이언트 파크(Bryant Park) 바로 옆에 있습니다. 도서관에서 공부하다가 바로 옆 공원에 가서 잠깐 쉬기도 좋은 위치에요.

    브라이언 공원
    브라이언트 공원

    브라이언트 파크(Bryant Park) 근처에는 블루보틀과 코워킹 스페이스인 WeWork 등이 있어요. 커피한잔 하면서 쉬어가기 좋은 뉴욕의 휴식처입니다.

  • 뉴욕 베슬 Vessel

    뉴욕 베슬 Vessel

    뉴욕의 새로운 랜드마크(?) 베슬을 다녀왔다. 허드슨 야드에 위치한 전망대 같은 건물로 사진 찍으러 가기 좋은 장소이다. 입장권이 있어야 들어갈 수 있는 데 사전에 온라인으로 예약하거나 현장에 있는 티켓 발권기를 통해 입장할 수 있다.

    온라인 예약은 Vessel을 클릭하면 된다.

    베슬 입장


    베슬
    뉴욕 베슬

    베슬의 가장 중앙에 사람들이 몰려있는 있다. 카메라를 바닥에 두고 내려다보면 베슬 건물 전체가 나오기 때문에 인기있는 사진 스팟이다.

    베슬
    베슬 전망대

    베슬의 위치는?


    베슬은 맨하튼에 위치하고 있고 하이라인 파크를 통해 근처까지 갈 수 있다.

  • Behind the scene of delightful experience

    Behind the scene of delightful experience

    I had presented about iOS animations at LetSwift conference last year. I classified the animations by researching the modern iOS app. There are 4 modern animation patterns.

    • Launch Animation

    • Loading Animation

    • View Transition

    • Interruptible Animation

    Before look at the animation patterns, Let’s take a look principle of animation. This principle is helping you when to decide the easing functions on your animation code.

    The principle of the animation

    The Illusion of Life: Disney Animation

    The Illusion of Life: Disney Animation

    In 1981, Frank Thomas and Ollie Johnston define the twelve principles of animations. I select the 4 principles which can reflect UI animation. These principles affect the easing functions in the Animation framework.

    The core animation framework has an easing function. We can set the easeIn, easeInOut, easeOut, and linear with duration time. Keep in mind these principles, I’ll show the examples and guides about easing function.

    This principle can reflect the launch animation, view transition and morphing icons.

    The secondary action principle reflects the physics effect, keyFrame animation, and interruptible animation.

    Easing functions

    easing.png

    Robert Penner is the creator of the easing function. It is widely used in various programming languages. The easing functions are basically inspired by twelve principles of animations. Let’s look at which easing function are suitable for UI.

    uber app

    uber app

    Look at Side Menu. The speed of showing and hiding the menu is different. Isn’t it?
    Here is my guide about the easing function.

    • Use ease-out when the view will appear.

      • duration 150ms – 350ms

    • Use ease-in or linear when the view will disappear.

      • duration 100ms – 150ms

     

    Modern iOS Animation

    Launch Animation

    The launch animation is the first impression and gives a delightful experience to the user. Technically, an iOS app is fetching data from the server, and It takes time to show the interface. So the fancy iOS apps use the launch animations while loading data.

    twitter.gif

    nike.gif

    uber.gif

    launch.gif

    I make the launch animation. I’ll post about how to make your launch animation.

    Loading Animation

    The loading animation displayed on the placeholder does not cover the screen, so it looks much cleaner.

    fbLoading.gif

    instagram.gif

    left_right_loading.gif

    I make the loading animation. I’ll post about how to make your loading animation.

    View Transition

    ViewTransition gives seamless experience compare to push view controller and present view controller, which is the default iOS presenting style. Technically you can implement your view transition style using UIViewControllerAnimatedTransitioning. I’ll post about UIViewControllerAnimatedTransitioning.

    zenly.gif

    nike.gif

    pinterest.gif

    instagram.gif

    Interruptible Animation

    You can see how Interruptible Animation works on the Apple Maps app. When you swipe up and down, then It will be starting Animation to expand the content. Interruptible Animation allows canceling while animating. Apple introduces this feature on iOS 10, and it called UIPropertyAnimator. I’ll also post about it soon.

    applemap.gif

     

    Conclusion

    I introduce the modern iOS animation styles. There are four kinds of patterns, and I’ll post how to implement these four patterns using CoreAnimation. If you want to get more information, then please look at references.

    References

    Google Fundamentals Design and UX

    Apple Core Animation Guide

    Disney 12 Principle Animations

    https://easings.net

    Understand-the-12-principles-of-animation

    Books

    Raywenderlich iOS Animation Book

    iOS Core Animation: Advanced Techniques

    Youtube

    Coding Math

    WWDC

    2017 Advances in UIKit Animations and Transitions