2020년 6월 11일 목요일

APNs Push Payload



1. alert notification

{
  "aps" : {
    "alert" : {
      "title" : "Message",
      "body" : "Your message here.1"
    },
    "badge" : 5,
    "sound": "default"
  }
}


2. background notification

{
"aps" : {
"content-available" : 1,
"badge" : 7
  }
}

2019년 1월 7일 월요일

drawing Shape with CAShapeLayer

import UIKit

class ShapeViewController: UIViewController {

    @IBOutlet var shapingView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func actionRect(_ sender: Any) {
        clear()
        doRectangle()
    }
    
    @IBAction func actionRoundedRect(_ sender: Any) {
        clear()
        doRoundedRectangle()
    }
    
    @IBAction func actionOptionalRoundedRect(_ sender: Any) {
        clear()
        doOptionalRoundedRectangle()
    }
    
    @IBAction func actionTriangle(_ sender: Any) {
        clear()
        doTriangle()
    }
    
    @IBAction func actionOval(_ sender: Any) {
        clear()
        doOval()
    }
    
    @IBAction func actionCircle(_ sender: Any) {
        clear()
        doCircle()
    }
    
    @IBAction func actionArc(_ sender: Any) {
        clear()
        doArc()
    }
    
    @IBAction func actionPie(_ sender: Any) {
        clear()
        doPie()
    }
    
    @IBAction func actionComplexShape() {
        clear()
        doComplexShape()
    }
    
    @IBAction func actionDonut(_ sender: Any) {
        clear()
        doDonut()
    }
    
    func degreeToRadian(_ degree: CGFloat) -> CGFloat {
        return degree * CGFloat(Double.pi) / 180.0
    }
    
    func clear() {
        shapingView.layer.sublayers?.forEach({ (layer) in
            layer.removeFromSuperlayer()
        })
    }
    
    // 직사각형
    func doRectangle() {
    #if false
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = UIBezierPath(rect: CGRect(x: 50, y: 50, width: 100, height: 100)).cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        canvas.layer.addSublayer(shapeLayer)
    #else
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 50, y: 50))
        path.addLine(to: CGPoint(x: 100, y: 150))
        path.addLine(to: CGPoint(x: 150, y: 150))
        path.addLine(to: CGPoint(x: 150, y: 50))
        path.close()
    #endif
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 0.5
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 모든 모서리가 둥근 사각형
    func doRoundedRectangle() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 50, y: 50, width: 200, height: 100), cornerRadius: 10).cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 1.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 선택적으로 모서리가 둥근 사각형
    func doOptionalRoundedRectangle() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 50, y: 50, width: 200, height: 100),
                                       byRoundingCorners: [.topLeft, .bottomRight],
                                       cornerRadii: CGSize(width: 15.0, height: 0.0)).cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 1.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 타원
    func doOval() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 200, height: 100)).cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 1.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 원
    func doCircle() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 100, height: 100)).cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 1.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 삼각형
    func doTriangle() {
        let trianglePath = UIBezierPath()
        trianglePath.move(to: CGPoint(x: shapingView.frame.width/2, y: 0.0))
        trianglePath.addLine(to: CGPoint(x: 0.0, y: shapingView.frame.size.height))
        trianglePath.addLine(to: CGPoint(x: shapingView.frame.size.width, y: shapingView.frame.size.height))
        trianglePath.close()
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = trianglePath.cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 2.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 호
    func doArc() {
        let shapeLayer = CAShapeLayer()
        let path = UIBezierPath(arcCenter: CGPoint(x: shapingView.bounds.width/2,
                                                          y: shapingView.bounds.height/2),
                                       radius: 100,
                                       startAngle: degreeToRadian(270),
                                       endAngle: degreeToRadian(45),
                                       clockwise: true)
        path.close()
        
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 2.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 파이
    func doPie() {
        let center = CGPoint(x: shapingView.bounds.width/2, y: shapingView.bounds.height/2)
        let radius: CGFloat = 80.0
        let path = UIBezierPath()
        path.move(to: center)
        path.addArc(withCenter: center,
                    radius: radius,
                    startAngle: degreeToRadian(270),
                    endAngle: degreeToRadian(135),
                    clockwise: true)
        path.close()
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = UIColor.blue.cgColor
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 2.0
        shapingView.layer.addSublayer(shapeLayer)
    }
    
    // 도넛
    func doDonut() {
        let centerPoint = CGPoint(x: shapingView.bounds.width/2,
                             y: shapingView.bounds.height/2)
        let radius: CGFloat = 80.0
        let lineWidth: CGFloat = 30.0
        
        // track
        let trackLayer = CAShapeLayer()
        trackLayer.path = UIBezierPath(arcCenter: centerPoint,
                                        radius: radius,
                                        startAngle: 0,
                                        endAngle: degreeToRadian(360), //CGFloat(Double.pi * 2.0),
                                        clockwise: true).cgPath
        trackLayer.fillColor = UIColor.clear.cgColor
        trackLayer.strokeColor = UIColor.white.cgColor
        trackLayer.lineWidth = lineWidth;
        shapingView.layer.addSublayer(trackLayer)
        
        // progress
        let progressLayer = CAShapeLayer()
        progressLayer.path = UIBezierPath(arcCenter: CGPoint(x: shapingView.bounds.width/2, y: shapingView.bounds.height/2),
                                       radius: radius,
                                       startAngle: degreeToRadian(270),
                                       endAngle:degreeToRadian(135),
                                       clockwise: true).cgPath
        progressLayer.fillColor = UIColor.clear.cgColor
        progressLayer.strokeColor = UIColor.red.cgColor
        progressLayer.lineWidth = lineWidth
        progressLayer.lineCap = CAShapeLayerLineCap.round // 양쪽 가장자리를 둥글게 표현
        shapingView.layer.addSublayer(progressLayer)
        
        // add animation to progress
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = 0.5
        animation.fromValue = 0.0
        animation.toValue = 1.0
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        progressLayer.add(animation, forKey: nil)
    }
    
    // 복잡한 모양
    func doComplexShape() {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0.0, y: 0.0))
        path.addLine(to: CGPoint(x: shapingView.frame.size.width/2 - 50.0, y: 0.0))
        path.addArc(withCenter: CGPoint(x: shapingView.frame.size.width/2 - 25.0, y: 25.0),
                    radius: 25.0,
                    startAngle: degreeToRadian(180.0),
                    endAngle: degreeToRadian(0.0),
                    clockwise: false)
        path.addLine(to: CGPoint(x: shapingView.frame.size.width/2, y: 0.0))
        path.addLine(to: CGPoint(x: shapingView.frame.size.width - 50.0, y: 0.0))
        path.addCurve(to: CGPoint(x: shapingView.frame.size.width, y: 50.0),
                      controlPoint1: CGPoint(x: shapingView.frame.size.width + 50.0, y: 25.0),
                      controlPoint2: CGPoint(x: shapingView.frame.size.width - 150.0, y: 50.0))
        path.addLine(to: CGPoint(x: shapingView.frame.size.width, y: shapingView.frame.size.height))
        path.addLine(to: CGPoint(x: 0.0, y: shapingView.frame.size.height))
        path.close()
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapingView.backgroundColor = UIColor.orange
        shapingView.layer.mask = shapeLayer
//        shapingView.layer.addSublayer(shapeLayer)
    }


}




Source : https://github.com/myunggu/Shape

drawing Shapes

import UIKit

class DrawingView: UIView {

    override func draw(_ rect: CGRect) {
//        let path: UIBezierPath = createOval()
        let path: UIBezierPath = createTriangle()
//        let path: UIBezierPath = createCircle()
        
        UIColor.orange.setFill()
        path.fill()
        
        UIColor.purple.setStroke()
        path.stroke()
    }
    
    
    // 타원
    func createOval() -> UIBezierPath {
        let path = UIBezierPath(ovalIn: self.bounds)
        return path
    }
    
    // 원
    func createCircle() -> UIBezierPath {
        let path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 100, height: 100))
        return path
    }
    
    // 삼각형
    func createTriangle() -> UIBezierPath {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: self.frame.width/2, y: 0.0))
        path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height))
        path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
        path.close()
        return path
    }
    
    // 사각형
    func createRectangle() -> UIBezierPath {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 50, y: 50))
        path.addLine(to: CGPoint(x: 100, y: 150))
        path.addLine(to: CGPoint(x: 150, y: 150))
        path.addLine(to: CGPoint(x: 150, y: 50))
        path.close()
        return path
    }
    
    // 모든 모서리가 둥근 사각형
    func createRoundedRectangle() -> UIBezierPath {
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 30.0)
        return path
    }
    
    // 선택적으로 모서리가 둥근 사각형
    func createOptionalRoundedRectangle() -> UIBezierPath {
        // cornerRadii : just the width is taken into account; the height is just disregarded.
        let path = UIBezierPath(roundedRect: self.bounds,
                            byRoundingCorners: [.topLeft, .bottomRight],
                            cornerRadii: CGSize(width: 30.0, height: 0.0))
        return path
    }
    
    // 호
    func createArc() -> UIBezierPath {
        let path = UIBezierPath(arcCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2),
                                radius: 50,
                                startAngle: degreeToRadian(180.0),
                                endAngle: degreeToRadian(0.0),
                                clockwise: true)
        return path
    }
    
    // 각도 -> 라디안
    func degreeToRadian(_ degree: CGFloat) -> CGFloat {
        return degree * CGFloat(Double.pi) / 180.0
    }


}


Source : https://github.com/myunggu/Shape

2018년 12월 27일 목요일

그룹핑 엑셀 UI 만들기





UITableView 또는 UICollectionView를 이용해서 위의 화면과같은 UI를 구현하기란 쉽지않다.
그래서 UIStackView를 사용해서 구현하면 좀 더 쉽게 구현할 수 있다.

UIScrollView에 UIStackView를 컨텐츠뷰의 서브뷰로 추가한다.
첫번째 그룹의 row UIStackView에 ArrangedSubView를 추가하지 않고 대신에 Intrinsic Size를 설정한다. 그 이유는 처음에 기본적으로 보여지는 row 없이 소스코드에서 추가하는 기능을 구현해보기 위해서다.

UIScrollView의 컨텐츠뷰의 centr Y를 뺀다.


마지막으로, 동적으로 추가할 셀을 만든다. 
StackCell.swift, StackCells.xib 두개의 파일을 생성한다.





소스코드는 다음과 같이 구현한다 :

GitHub



class Sub2ViewController: UIViewController {
    @IBOutlet var cellStackView1: UIStackView! //Group1
    @IBOutlet var cellStackView2: UIStackView! //Group2
    @IBOutlet var cellStackView3: UIStackView! //Group3
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 첫번째 그룹에 row 추가
        addCell(to: cellStackView1)
        addCell(to: cellStackView1)
        addCell(to: cellStackView1)
        addCell(to: cellStackView1)
        
        // 두번째 그룹에 row 추가
        addCell(to: cellStackView2)
        addCell(to: cellStackView2)
        addCell(to: cellStackView2)
        addCell(to: cellStackView2)
        addCell(to: cellStackView2)
        addCell(to: cellStackView2)
        addCell(to: cellStackView2)
        
        // 세번째 그룹에 row 추가
        addCell(to: cellStackView3)
        addCell(to: cellStackView3)
        addCell(to: cellStackView3)
        addCell(to: cellStackView3)
        addCell(to: cellStackView3)
        addCell(to: cellStackView3)
    }
    
    @IBAction func actionRemove(_ sender: Any) {
        removeCell(to: cellStackView1)
    }
    
    @IBAction func actionAdd(_ sender: Any) {
        addCell(to: cellStackView1)
    }
    
    
    
    func addCell(to stackView: UIStackView) {
        let cell = Bundle.main.loadNibNamed("StackCells", owner: self, options: nil)![0] as! StackCell
        cell.heightAnchor.constraint(equalToConstant: 50).isActive = true
        cell.label.text = "Cell \(stackView.arrangedSubviews.count)"
        stackView.addArrangedSubview(cell)
        
    }
    
    func removeCell(to stackView: UIStackView) {
        let cell = stackView.arrangedSubviews.first!
        stackView.removeArrangedSubview(cell)
        cell.removeFromSuperview()
    }

}





class StackCell: UIView {
    @IBOutlet var label: UILabel!


}