Prática 3: Criando uma View Personalizada

Assista e acompanhe a prática em sala dos exercícios "Criando uma View Personalizada". Execute as instruções conforme a apresentação.

Exercício 1: Criando o Controle de Início da Sessão

1.1. Criar a classe LongPressButton

Criar a classe com o nome LongPressButton como subclasse de UIControl no grupo Views.

1.2. Tornar a Classe IBDesignable

Ajustar o código da classe para torna-la IBDesignable conforme abaixo:

@IBDesignable class LongPressButton: UIControl {

}

1.3. Declarar as propriedades da classe

Declarar as propriedades da classe conforme o código abaixo:

//
// MARK: - Propriedades

private let progressLine = CAShapeLayer()
private let basicAnimate = CABasicAnimation(keyPath: "strokeEnd")
private var ovalPath: UIBezierPath!

var backgroundImage: UIImageView!

@IBInspectable var pressDelay: Double = 3.0 {
    didSet {
        self.basicAnimate.duration = self.pressDelay
    }
}

1.4. Incluir os métodos de apoio

Incluir os métodos de apoio conforme o código abaixo:

//
// MARK: - Métodos de Apoio

/// Inicializa os componentes da View
func initializeView() {
    // Configura o layer do círculo que será animado em volta do ícone
    self.progressLine.strokeColor = UIColor(red: 1/255, green: 84/255, blue:139/255, alpha: 1.0).CGColor
    self.progressLine.fillColor = UIColor.clearColor().CGColor
    self.progressLine.lineWidth = 17.0
    self.progressLine.lineCap = kCAFillRuleNonZero

    // Configura o objeto que irá animar o círculo em volta do ícone
    self.basicAnimate.delegate = self
    self.basicAnimate.duration = self.pressDelay
    self.basicAnimate.fromValue = 0.0
    self.basicAnimate.toValue = 1.0
    self.basicAnimate.fillMode = kCAFillModeForwards
    self.basicAnimate.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

    // Carrega as Sub-Views
    let bundle = NSBundle(forClass: self.dynamicType)
    self.backgroundImage = UIImageView(image: UIImage(named: "LongPressButton_Background", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection))
    self.addSubview(self.backgroundImage)
}

/// Configura o objeto *ovalPath* com um Path circula que toma a largura e altura da View
func configurePath() {
    // set up some values to use in the curve
    let ovalStartAngle = CGFloat(90.01 * M_PI/90)
    let ovalEndAngle = CGFloat(90 * M_PI/90)
    let ovalRect = CGRectMake(
        self.frame.size.width / 2,
        self.frame.size.height / 2,
        self.frame.size.width - 30,
        self.frame.size.height - 30)

    // Set values to the bezier path
    self.ovalPath = UIBezierPath()
    self.ovalPath.addArcWithCenter(CGPointMake(self.frame.size.width/2, self.frame.size.height/2),
        radius: CGRectGetWidth(ovalRect) / 2,
        startAngle: ovalStartAngle,
        endAngle: ovalEndAngle, clockwise: true)

    // create an object that represents how the curve
    // should be presented on the screen
    self.progressLine.path = ovalPath.CGPath
}

1.5. Declarar os inicializadores

Incluir o código dos inicializadores:

//
// MARK: - Initializers

override init(frame: CGRect) {
    super.init(frame: frame)
    self.initializeView()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.initializeView()
}

1.6. Incluir os Overrides

Inclua os overrides necessários conforme o código abaixo:

//
// MARK: - Overrides

override func intrinsicContentSize() -> CGSize {
    return self.backgroundImage.intrinsicContentSize()
}

override func layoutSubviews() {
    super.layoutSubviews()
    self.backgroundImage.frame = self.bounds
    self.configurePath()
}

1.7. Incluir métodos de apoio para animações

Incluir os métodos conforme o código abaixo:

//
// MARK - Métodos de apoio para animação

func startAnimate() {
    self.layer.addSublayer(progressLine)
    progressLine.addAnimation(basicAnimate, forKey: "animate stroke end animation")
}

func stopAnimate(){
    self.progressLine.removeAllAnimations()
    self.progressLine.removeFromSuperlayer()
}

func pressHandled() {
    self.stopAnimate()
}

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
    if flag {
        sendActionsForControlEvents(UIControlEvents.TouchUpOutside)
    }
}

1.8. Incluir os métodos do UIResponderDelegate

Incluir os métodos conforme o código abaixo:

//
// MARK - UIResponder Delegate

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    sendActionsForControlEvents(UIControlEvents.TouchUpInside)
    self.startAnimate()
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.stopAnimate()
}

1.9. Copiar os Assets do Controle

Baixar o arquivo LongPressButton.zip a partir deste link. Descompacte e copie para o catálogo de imagens do projeto.


Exercício 2: Redesenhando a tela de Início

2.1. Localizar o View Controller de Início no Storyboard

2.2. Remover o Botão Iniciar Atividade

2.3. Remover as Constraints do Picker View

Selecionar o Picker View e remover suas constraints usando o menu Resolve Auto Layout Issues.

2.4. Posicionar o Picker na Parte Inferior da Tela

Colocar o Packer View alinhado com a parte inferior da tela. Incluir as Constraints:

  • Leading Space to: Superview
  • Trailing Space to: Superview
  • Bottom Space to: Bottom Layout Guide

2.5. Desenhar um UIView para o Controle acima do Picker View

2.6. Configurar a propriedade Class do Controle

Configurar a propriedade Class do Controle para LongPressButton.

2.7. Incluir as Constraints do Controle

Incluir as seguintes Constraints para o Controle:

  • Align Center X to: Superview
  • Bottom Spacing to: Picker View

Ajustar os frames.

2.8. Ajustar o Action Method

Ajustar o Action method conforme o Snippet abaixo, substitua:

@IBAction func startSession(sender: UIButton) {

Por:

@IBAction func startSession(sender: LongPressButton) {

2.9. Incluir o Código no final do Action Method

sender.pressHandled()

2.10. Associar o evento Touch Up Outisde do Controle ao Action Method

2.11. Modifique o tempo da animação para 1 segundo