I'm trying to make an animated UILabel using CATransition which can fade out the original text and fade in the new text when I touch the screen. Here is my code and I can't figure out why there is no animation. Please help me. I'm using Xcode 7.3.
var subtitle:UILabel!
var winsize:CGSize!
override func viewDidLoad() {
super.viewDidLoad()
let animation = CATransition()
animation.type = kCATransitionFade
animation.duration = 0.75
animation.fillMode = kCAFillModeBoth
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
animation.delegate = self
self.subtitle = UILabel()
self.subtitle.text = "f"
self.subtitle.frame = CGRectMake(45, 30, 200, 50)
self.subtitle.font = UIFont.systemFontOfSize(25)
self.subtitle.textColor = UIColor.grayColor()
self.view.addSubview(self.subtitle)
self.subtitle.layer.addAnimation(animation, forKey: "animation")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.subtitle.text="HighSchool"
}
With help from #matt, the following code works.
var subtitle:UILabel!
var winsize:CGSize!
override func viewDidLoad() {
super.viewDidLoad()
winsize = self.view.frame.size
self.subtitle = UILabel()
self.subtitle.text = "f"
self.subtitle.frame = CGRectMake(45, 30, 200, 50)
self.subtitle.font = UIFont.systemFontOfSize(25)
self.subtitle.textColor = UIColor.grayColor()
self.view.addSubview(self.subtitle)
UIView.transitionWithView(self.subtitle, duration: 0.5, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: nil, completion: nil)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.subtitle.text="HighSchool"
}
Like this:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let opts : UIViewAnimationOptions = .TransitionCrossDissolve
UIView.transitionWithView(self.subtitle, duration: 0.75, options: opts, animations: {
self.subtitle.text="HighSchool"
}, completion: nil)
}
Related
I am implementing the chat bubble feature. I want the chat bubble to be on top of all the view controller and shouldn't be affected by screen transition. So I decided to add it as subview to UIWindow so far it looks like
This works great, but if I present view controller instead of push then chat head goes behind the presented view controller. To avoid it I set ZIndex property of chat head layer
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let window = UIApplication.shared.keyWindow {
window.addSubview(chatHead)
chatHead.layer.zPosition = 1
}
}
But now though chat head appears on top of presented view controller, its touch delegates like touchesBegan, touchesMoved, touchesEnded nothing gets called when chat head is above presented ViewController
Adding code of chat head (lot of code might be unnecessary to this question, but just posting it so if someone would like to try it)
class ChatHeadView: UIView {
let headerWidth: CGFloat = 80.0
let headerHeight: CGFloat = 80.0
let sideInset: CGFloat = 10.0
let bottomInset: CGFloat = 10.0
var cancellationWindowShown = false
lazy var cancellationContainerRect: CGRect = {
var rect = self.cancelChatView.convert(self.cancelChatView.frame, to: nil)
rect.origin.x = rect.origin.x + 30
rect.size.height = 100
return rect
}()
lazy var cancellationThreshold: CGFloat = {
return self.keywindow.frame.maxY - (self.keywindow.frame.maxY / 3)
}()
lazy var keywindow: UIWindow = {
let window = UIApplication.shared.keyWindow ?? UIWindow()
return window
}()
lazy var cancelChatView: CancelChatView = {
if let cancelView = Bundle.main.loadNibNamed("CancelChatView", owner: nil, options: [:])?.first as? CancelChatView {
return cancelView
}
fatalError("Couldnt load")
}()
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
self.layer.masksToBounds = true
self.layer.cornerRadius = 40
self.layer.backgroundColor = UIColor.red.cgColor
self.cancelChatView.frame = CGRect(x: 0, y: self.keywindow.frame.maxY - 140, width: self.keywindow.frame.width, height: 140)
self.cancelChatView.layer.opacity = 0.0
self.keywindow.addSubview(self.cancelChatView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
debugPrint("Touch started")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self.keywindow)
if location.y >= cancellationThreshold {
self.showCancellationWindow()
}
else {
removeCancellationView()
debugPrint("Removing Cancellation window")
}
self.center = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self.keywindow)
self.center = location
self.snap(to: location)
self.removeCancellationView()
}
}
private func snap(to point: CGPoint) {
var finalPoint:CGPoint = self.frame.origin
if self.cancellationContainerRect.intersects(self.convert(self.frame, to: nil)) {
self.removeFromSuperview()
}
else {
if finalPoint.x <= self.keywindow.center.x {
finalPoint.x = self.keywindow.frame.minX + sideInset
}
else if finalPoint.x > self.keywindow.center.x {
finalPoint.x = self.keywindow.frame.maxX - (sideInset + self.headerWidth)
}
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.6, options: .curveEaseInOut, animations: {
self.frame.origin = finalPoint
}, completion: nil)
}
}
private func showCancellationWindow() {
if cancellationWindowShown == false {
let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
animation.fromValue = 0.0
animation.toValue = 0.6
animation.duration = 0.1
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
self.cancelChatView.layer.add(animation, forKey: "reveal")
self.cancellationWindowShown = true
}
}
private func removeCancellationView() {
if cancellationWindowShown == true {
let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
animation.toValue = 0.0
animation.duration = 0.1
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
self.cancelChatView.layer.add(animation, forKey: "hide")
cancellationWindowShown = false
}
}
}
Issue Gif (touch not working):
As matt has asked for details of how I am presenting ViewController, adding screenshot of storyboard segue configuration
Wanted to know how I can drag a image across screen and what code would be used. Tried looking up but only older versions of Swift have answer and no longer work. I want to drag the image, but not place finger on screen and it goes to that spot. Just drag.
Gives me the error:
"Use of undeclared type 'uitouch'"
import UIKit
class DraggableImage: UIImageView {
override func touchesMoved(touches: Set<uitouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position = touch.locationInView(superview)
center = CGPointMake(position.x, position.y)
}
}
}
You need to subclass UIImageView and in the init you need to set userInteractionEnabled = true and then override this method override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) well, my code is this:
class DraggableImage: UIImageView {
var localTouchPosition : CGPoint?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.red.cgColor
self.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
self.localTouchPosition = touch?.preciseLocation(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.frame.origin = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.localTouchPosition = nil
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
This is how it looks
Hope this helps
Create a Nsobject Class for moving View and add following Code
import UIKit
class objectClass: UIImageView, UIGestureRecognizerDelegate {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
self.center = touch.location(in: self.superview)
}
}
in mainViewController make a object of NSobject class
var newView: objectClass = objectClass()
on button Action to add new View
#IBAction func objectAdded(theButton: UIButton!) {
let frame = CGRect(x: 100, y: 100, width: 44, height: 44)
newView = objectClass(frame: frame)
if theButton.titleLabel?.text == "image1" {
newView.image = UIImage(named: "1")
} else if theButton.titleLabel?.text == "image2" {
newView.image = UIImage(named: "2")
}else{
newView.image = UIImage(named: "3")
}
newView.contentMode = .scaleAspectFill
newView.isUserInteractionEnabled = true
self.view .addSubview(newView)
newView.alpha = 0
UIView .animate(withDuration: 0.4) {
self.newView.alpha = 1
}
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseOut, animations: { () -> Void in
self.sliderViewBottomLayoutConstraint.constant = self.sliderViewBottomLayoutConstraint.constant - self.sliderViewBottomLayoutConstraint.constant
self.view.layoutIfNeeded()
}, completion: nil)
image1Button.isEnabled = false
image2Button.isEnabled = false
image3Button.isEnabled = false
let pinchGesture: UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(ViewController.recognizePinchGesture(sender:)))
pinchGesture.delegate = self
let rotateGesture: UIRotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(ViewController.recognizeRotateGesture(sender:)))
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.RemoveSelectedImageOnTap(sender:)))
tapGesture.numberOfTapsRequired = 2
self.newView.addGestureRecognizer(tapGesture)
self.newView.addGestureRecognizer(pinchGesture)
self.newView.addGestureRecognizer(rotateGesture)
}
func recognizePinchGesture(sender: UIPinchGestureRecognizer) {
sender.view!.transform = sender.view!.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
func recognizeRotateGesture(sender: UIRotationGestureRecognizer) {
sender.view!.transform = sender.view!.transform.rotated(by: sender.rotation)
sender.rotation = 0
}
In my application i have a big button. I want it to have a glowing effect around it.
Can it be done using animations?
I have tried using the image but it does not looks clean and appealing.
I had been working in your question and this is my results,I define a custom subclass of UIButton,adding a CABasicAnimation animating shadowRadius property, setting autoreverses to true and repeatCount to infinity
Code
//
// GlowingButton.swift
// NavigationButtonRotateQuestion
//
// Created by Reinier Melian on 01/07/2017.
// Copyright © 2017 Pruebas. All rights reserved.
//
import UIKit
#IBDesignable
class GlowingButton: UIButton {
#IBInspectable var animDuration : CGFloat = 3
#IBInspectable var cornerRadius : CGFloat = 5
#IBInspectable var maxGlowSize : CGFloat = 10
#IBInspectable var minGlowSize : CGFloat = 0
#IBInspectable var glowColor : UIColor = nil ?? UIColor.red
#IBInspectable var animateAlways : Bool = false
fileprivate var animating : Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.contentScaleFactor = UIScreen.main.scale
self.layer.masksToBounds = false
if(self.animateAlways){
self.setupButtonForContinueAnimation()
self.startAnimation()
}else{
self.setupButton()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!self.animateAlways){
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = minGlowSize
layerAnimation.toValue = maxGlowSize
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
self.layer.add(layerAnimation, forKey: "addGlowing")
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!self.animateAlways){
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = maxGlowSize
layerAnimation.toValue = minGlowSize
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
self.layer.add(layerAnimation, forKey: "removeGlowing")
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!self.animateAlways){
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = maxGlowSize
layerAnimation.toValue = minGlowSize
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
self.layer.add(layerAnimation, forKey: "removeGlowing")
}
}
func setupButton()
{
self.layer.cornerRadius = cornerRadius
self.layer.shadowPath = CGPath(roundedRect: self.bounds, cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)
self.layer.shadowRadius = 0
self.layer.shadowColor = self.glowColor.cgColor
self.layer.shadowOffset = CGSize.zero
self.layer.shadowOpacity = 1
}
func setupButtonForContinueAnimation()
{
self.setupButton()
self.layer.shadowRadius = maxGlowSize
}
func startAnimation()
{
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = maxGlowSize
layerAnimation.toValue = minGlowSize
layerAnimation.autoreverses = true
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
layerAnimation.repeatCount = .infinity
self.layer.add(layerAnimation, forKey: "glowingAnimation")
}
func stopAnimations() {
self.layer.removeAllAnimations()
}
}
Edited: Added Inspectable attributes for better customization
Edited: Adapted to swift4 and added func to stop animation
I have a simple pong-like game and so far everything is working well. However I'd like to count the number of times the ball has hit the "paddle" as sort of a high-score. Here's the code for the sliderBar (paddle) touch events:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if !self.isBallRolling {
let pushBehavior = UIPushBehavior(items: [self.ball], mode: .instantaneous)
pushBehavior.magnitude = 1.5
self.animator.addBehavior(pushBehavior)
self.isBallRolling = true
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch? = touches.first
let touchLocation: CGPoint? = touch?.location(in: self.view)
let yPoint: CGFloat = self.sliderBarCenterPoint.y
let sliderBarCenter = CGPoint(x: CGFloat((touchLocation?.x)!), y: yPoint)
self.sliderBar.center = sliderBarCenter
self.animator.updateItem(usingCurrentState: self.sliderBar)
}
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item1: UIDynamicItem, with item2: UIDynamicItem, at p: CGPoint) {
let pushBehavior = UIPushBehavior(items: [self.ball], mode: .instantaneous)
pushBehavior.angle = 0.0
pushBehavior.magnitude = 0.75
self.animator.addBehavior(pushBehavior)
}
// The label that displays the score
let countLabel: UILabel = {
let frame = CGRect(x: 100, y: 100, width: 200, height: 50)
let cl = UILabel(frame: frame)
cl.textAlignment = NSTextAlignment.center
cl.text = ""
cl.textColor = UIColor.red
cl.translatesAutoresizingMaskIntoConstraints = false
return cl
}()
func setupCountLabel() {
countLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
countLabel.bottomAnchor.constraint(equalTo: self.sliderBar.topAnchor, constant: -3).isActive = true
countLabel.widthAnchor.constraint(equalToConstant: 150).isActive = true
countLabel.heightAnchor.constraint(equalToConstant: 73).isActive = true
}
I've never worked with UICollisionBehaviorDelegate before - where and how should I be detecting the amount of times the ball has hit the sliderBar?
Thanks for any help!
I am using SpriteKit animation in my iOS application at first time. My aim is to drag and drop label, image and textfield. I have done it for label and image using SKLabelNode and SKSpriteNode.
Globally I declared,
var deltaPoint = CGPointZero
var selectedNode = SKNode()
First I created label, image, textfield in didMoveToView method. Then added label and image as child node and textfield as subview like below mentioned code.
let textField = UITextField(frame: CGRectMake(self.size.width / 2, self.size.height / 2 + 20, 200, 40))
textField.center = view.center
textField.borderStyle = .RoundedRect
textField.tag = 101
textField.textColor = UIColor.blackColor()
textField.font = UIFont.systemFontOfSize(17.0)
textField.placeholder = "Enter your name here"
textField.backgroundColor = UIColor.whiteColor()
textField.autocorrectionType = .Yes
textField.keyboardType = .Default
textField.clearButtonMode = .WhileEditing
textField.enabled = true
view.addSubview(textField)
let imageNode = SKSpriteNode(imageNamed: "images.jpg")
imageNode.name = kMovableImageNodeName
imageNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(imageNode)
let labelNode = SKLabelNode(fontNamed: "Helvetica")
labelNode.name = kMovableLabelNodeName
labelNode.text = "Welcome"
labelNode.fontSize = 30
labelNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(labelNode)
In TouchesBegan method, set touched node to selected node.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
labelNode.zPosition = 0
imageNode.zPosition = 0
let positionInScene = touches.first?.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene!)
selectedNode = touchedNode
selectedNode.zPosition = 1
}
In TouchesMoved method, setting delta point value
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let currentPoint = touches.first?.locationInNode(self)
let previousPoint :CGPoint = (touches.first?.previousLocationInNode(self))!
deltaPoint = CGPointMake(currentPoint!.x - previousPoint.x, currentPoint!.y - previousPoint.y)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
selectedNode.zPosition = 1
deltaPoint = CGPointZero
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
selectedNode.zPosition = 0
deltaPoint = CGPointZero
}
In update method, setting new point to selected node
override func update(currentTime: CFTimeInterval) {
let newPoint = CGPointMake(selectedNode.position.x + self.deltaPoint.x, self.selectedNode.position.y + self.deltaPoint.y)
selectedNode.position = newPoint
deltaPoint = CGPointZero
}
Like this how to drag and drop the textfield and when user double tapped the textfield, have to show keyboard.