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()