I am facing issue in frame while doing rotation animation in custom UIBarButtonItem.
My code is as follow:
class ViewController: UIViewController {
var bellIcon: UIBarButtonItem = UIBarButtonItem()
override func viewDidLoad() {
super.viewDidLoad()
setupBellButton()
}
func setupBellButton(){
let icon = UIImage(named: "Bell.png")
let iconSize = CGRect(origin: CGPoint.zero, size: icon!.size)
let iconButton = UIButton(frame: iconSize)
iconButton.setBackgroundImage(icon, for: .normal)
let badgeView = UIView(frame: CGRect(origin: CGPoint(x: iconButton.frame.maxX-10, y: iconButton.frame.minX), size: CGSize(width: 10, height: 10)))
badgeView.layer.cornerRadius = 5
badgeView.layer.borderColor = UIColor.red.cgColor
badgeView.backgroundColor = UIColor.red
badgeView.layer.masksToBounds = true
badgeView.clipsToBounds = true
let btnContainer = UIView(frame: CGRect(origin: CGPoint.zero, size: icon!.size))
btnContainer.addSubview(iconButton)
btnContainer.addSubview(badgeView)
badgeView.transform = CGAffineTransform(scaleX: 0, y: 0)
UIView.animate(withDuration: 1) {
badgeView.transform = CGAffineTransform(scaleX: 1, y: 1)
}
bellIcon.customView = btnContainer
iconButton.addTarget(self, action:#selector(bellIconTapped), for: .touchUpInside)
navigationItem.rightBarButtonItem = bellIcon
bellIcon.customView?.backgroundColor = UIColor.purple
}
#IBAction func bellIconTapped(_ sender: Any) {
UIView.animate(withDuration: 0.3, animations: {
self.bellIcon.customView!.transform = CGAffineTransform(rotationAngle: -0.4)
}) { (true) in
UIView.animate(withDuration: 0.3, animations: {
self.bellIcon.customView!.transform = CGAffineTransform(rotationAngle: 0.4)
}, completion: { (true) in
UIView.animate(withDuration: 0.4, animations: {
self.bellIcon.customView!.transform = CGAffineTransform.identity
})
})
}
}
}
Thanks in advance!!!
I have resolved the above issue for both iOS 10 and 11 by simply adding the constant to the 'bellIcon.customView'
I have added following code at the end of the 'setupBellButton()'
bellIcon.customView?.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = bellIcon.customView?.widthAnchor.constraint(equalToConstant: 25.0)
let heightConstraint = bellIcon.customView?.heightAnchor.constraint(equalToConstant: 25.0)
bellIcon.customView?.addConstraints([widthConstraint!, heightConstraint!])
Happy Coding!!!
Related
I have a Pan and Tap gestures, They don't seem to get recognized when I try to activate them on the simulator.
I don't get any error, tried to debugging, it seems that it does assign the gestures, but won't call the methods I try to use when they happened, like it just don't recognize them at all.
This is what I have:
dismissIndicator: (Attaching the Pan recognizer to this view):
let dismissIndicator: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.appWhite
view.layer.cornerRadius = 15
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
let dismissImg = UIImageView(image: UIImage(named: "closeArrow"))
dismissImg.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dismissImg)
dismissImg.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dismissImg.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
return view
}()
This is how I set the gestures:
let dimView = UIView()
let detailView = DetailsView()
var currentPost: Post? = nil
init(post: Post) {
super.init()
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
let panGesture = PanDirectionGestureRecognizer(direction: .vertical(.down), target: self, action: #selector(panGestureRecognizerHandler(_:)))
detailView.addGestureRecognizer(panGesture)
setPostDetails(post: post)
}
This is handleDismiss (Which does not get called when tapping on the DimView:
#objc func handleDismiss(){
UIView.animate(withDuration: 0.5) {
self.dimView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.detailView.frame = CGRect(x: 0, y: window.frame.height, width: self.detailView.frame.width, height: self.detailView.frame.height)
}
}
}
This is my panGestureRecognizerHandler:
#objc func panGestureRecognizerHandler(_ sender: UIPanGestureRecognizer) {
let touchPoint = sender.location(in: detailView.dismissIndicator.window)
var initialTouchPoint = CGPoint.zero
switch sender.state {
case .began:
initialTouchPoint = touchPoint
case .changed:
if touchPoint.y > initialTouchPoint.y {
detailView.frame.origin.y = initialTouchPoint.y + touchPoint.y
}
case .ended, .cancelled:
if touchPoint.y - initialTouchPoint.y > 300 {
handleDismiss()
} else {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
if let window = UIApplication.shared.keyWindow {
let height = window.frame.height - 80
let y = window.frame.height - height
self.dimView.alpha = 1
self.detailView.frame = CGRect(x: 0, y: y, width: self.detailView.frame.width, height: self.detailView.frame.height)
}
})
}
case .failed, .possible:
break
default:
break
}
}
This is where I add dimView and detailView to the screen:
func showDetailView(){
if let window = UIApplication.shared.keyWindow {
dimView.backgroundColor = UIColor(white: 0, alpha: 0.5)
window.addSubview(dimView)
window.addSubview(detailView)
let height = window.frame.height - 80
let y = window.frame.height - height // This is so we can later slide detailView all the way down.
detailView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
dimView.frame = window.frame
dimView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.dimView.alpha = 1
self.detailView.frame = CGRect(x: 0, y: y, width: self.detailView.frame.width, height: self.detailView.frame.height)
})
}
}
When user open my app for the first time, my custom view doesn't appear. But the next time it works fine.
I am calling this view when user taps on button.
let menu = MenuView(image: image, title: "text", buttons: buttons)
menu.show(animated: true)
Custom View code
class MenuView: UIView, Menu {
var background = UIView()
var blackOverlay = UIView()
convenience init(image: UIImage, title: String, buttons: [UIButton]) {
self.init(frame: UIScreen.main.bounds)
setupView(image: image, title: title, buttons: buttons)
}
#objc func cancelTapped() {
hide(animated: true)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView(image: UIImage, title: String, buttons: [UIButton]) {
blackOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5)
blackOverlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(blackOverlay)
let backgroundWidth = self.frame.width - CGFloat(80.0)
let imageView = UIImageView(frame: CGRect(x: (backgroundWidth/2)-17, y: 40, width: 34, height: 34))
imageView.image = image
imageView.contentMode = .center
background.addSubview(imageView)
let titleLabel = UILabel()
let stringHeight = title.stringHeight + 14
titleLabel.frame = CGRect(x: background.center.x+8, y: 100, width: backgroundWidth-16, height: stringHeight)
titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .regular)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
background.addSubview(titleLabel)
var newHeight: CGFloat = 0
for i in 0...buttons.count-1 {
buttons[i].frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
buttons[i].frame.size = CGSize(width: backgroundWidth, height: 50)
buttons[i].setTitleColor(.gingerColor, for: .normal)
buttons[i].setTitleColor(UIColor.gingerColor.withAlphaComponent(0.5), for: .highlighted)
buttons[i].titleLabel?.textAlignment = .center
newHeight+=buttons[i].frame.height
let separator = UIView()
separator.frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
separator.frame.size = CGSize(width: frame.width, height: 1)
separator.backgroundColor = UIColor(hexString: "dedede")
background.addSubview(separator)
if i == buttons.count-1 {
buttons[i].setTitleColor(UIColor(hexString: "9E9E9E"), for: .normal)
}
buttons[i].addTarget(self, action: #selector(self.cancelTapped), for: .touchUpInside)
background.addSubview(buttons[i])
}
background.frame.origin = CGPoint(x: center.x, y: frame.height)
background.frame.size = CGSize(width: frame.width-80, height: 90 + titleLabel.frame.height+imageView.frame.height + CGFloat(newHeight))
background.backgroundColor = .white
background.layer.cornerRadius = 16
background.layer.masksToBounds = true
addSubview(background)
}
}
and my custom view protocol & extension code:
protocol Menu {
func show(animated: Bool)
func hide(animated: Bool)
var blackOverlay: UIView { get }
var background: UIView { get }
}
extension Menu where Self: UIView {
func show(animated: Bool) {
self.blackOverlay.alpha = 0
self.background.alpha = 0
self.blackOverlay.center = self.center
self.background.center = CGPoint(x: self.center.x, y: self.frame.height+self.background.frame.height/2)
UIApplication.shared.delegate?.window??.rootViewController?.view.addSubview(self)
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 1
self.background.alpha = 1
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = self.center
}, completion: { (completed) in
print("completed is \(completed)")
})
} else {
self.blackOverlay.alpha = 1
self.background.alpha = 1
self.background.center = self.center
}
}
func hide(animated: Bool) {
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 0
self.background.alpha = 0
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = CGPoint(x: self.center.x, y: self.frame.height + self.background.frame.height/2)
}, completion: { (completed) in
self.removeFromSuperview()
})
} else {
self.removeFromSuperview()
}
}
}
As you can see I have a parameter completed in completion animation block, it returns false for the first time. All subsequent times it returns false
I found my mistake.
I tried to present my custom view controller above rootController. But, for the very first time user's rootcontroller is the first screen of onboarding. So, my custom view tried to be shown on another controller.
I want to add UITableView on top of UIView which is a subclass of GooeySlideMenu. As in example shown in git I created UITableView instead of UIButtons but delegate methods are not called.
Below is my code for reference:
class GooeySlideMenu: UIView {
fileprivate var _option: MenuOptions
fileprivate var keyWindow: UIWindow?
fileprivate var blurView: UIVisualEffectView!
fileprivate var helperSideView: UIView!
fileprivate var helperCenterView: UIView!
fileprivate var diff: CGFloat = 0.0
fileprivate var triggered: Bool = false
fileprivate var displayLink: CADisplayLink?
fileprivate var animationCount: Int = 0
fileprivate var myTableView: tableViewCustomClass = tableViewCustomClass()
init(options: MenuOptions) {
_option = options
if let kWindow = UIApplication.shared.keyWindow{
keyWindow = kWindow
let frame = CGRect(
x: -kWindow.frame.size.width/2 - options.menuBlankWidth,
y: 0,
width: kWindow.frame.size.width/2 + options.menuBlankWidth,
height: kWindow.frame.size.height)
super.init(frame:frame)
} else {
super.init(frame:CGRect.zero)
}
setUpViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: frame.width-_option.menuBlankWidth, y: 0))
path.addQuadCurve(to: CGPoint(x: frame.width-_option.menuBlankWidth, y: frame.height), controlPoint: CGPoint(x: frame.width-_option.menuBlankWidth+diff, y: frame.height/2))
path.addLine(to: CGPoint(x: 0, y: frame.height))
path.close()
let context = UIGraphicsGetCurrentContext()
context?.addPath(path.cgPath)
_option.menuColor.set()
context?.fillPath()
}
func trigger() {
if !triggered {
if let keyWindow = keyWindow {
keyWindow.insertSubview(blurView, belowSubview: self)
UIView.animate(withDuration: 0.3, animations: { [weak self] () -> Void in
self?.frame = CGRect(
x: 0,
y: 0,
width: keyWindow.frame.size.width/2 + (self?._option.menuBlankWidth)!,
height: keyWindow.frame.size.height)
})
beforeAnimation()
UIView.animate(withDuration: 0.7, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.9, options: [.beginFromCurrentState,.allowUserInteraction], animations: { [weak self] () -> Void in
self?.helperSideView.center = CGPoint(x: keyWindow.center.x, y: (self?.helperSideView.frame.size.height)!/2);
}, completion: { [weak self] (finish) -> Void in
self?.finishAnimation()
})
UIView.animate(withDuration: 0.3, animations: { [weak self] () -> Void in
self?.blurView.alpha = 1.0
})
beforeAnimation()
UIView.animate(withDuration: 0.7, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 2.0, options: [.beginFromCurrentState,.allowUserInteraction], animations: { [weak self] () -> Void in
self?.helperCenterView.center = keyWindow.center
}, completion: { [weak self] (finished) -> Void in
if finished {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(GooeySlideMenu.tapToUntrigger))
self?.blurView.addGestureRecognizer(tapGesture)
self?.finishAnimation()
}
})
// animateButtons()
myTableView.reloadData()
triggered = true
}
} else {
tapToUntrigger()
}
}
}
extension GooeySlideMenu {
fileprivate func setUpViews() {
if let keyWindow = keyWindow {
blurView = UIVisualEffectView(effect: UIBlurEffect(style: _option.blurStyle))
blurView.frame = keyWindow.frame
blurView.alpha = 0.0
helperSideView = UIView(frame: CGRect(x: -40, y: 0, width: 40, height: 40))
helperSideView.backgroundColor = UIColor.red
helperSideView.isHidden = true
keyWindow.addSubview(helperSideView)
helperCenterView = UIView(frame: CGRect(x: -40, y: keyWindow.frame.height/2 - 20, width: 40, height: 40))
helperCenterView.backgroundColor = UIColor.yellow
helperCenterView.isHidden = true
keyWindow.addSubview(helperCenterView)
backgroundColor = UIColor.clear
keyWindow.insertSubview(self, belowSubview: helperSideView)
addUItableView()
// addButton()
}
}
fileprivate func addUItableView(){
myTableView.frame = CGRect(x: 0, y: 20, width: 300, height: 200)
myTableView.backgroundColor = UIColor.white
myTableView.delegate = tableViewCustomClass() as? UITableViewDelegate
myTableView.dataSource = tableViewCustomClass() as? UITableViewDataSource
addSubview(myTableView)
}
You need to declared tableview's delegate on tableViewCustomClass's class instead of GooeySlideMenu class .
In tableViewCustomClass
self.delegate = self in tableViewCustomClass's initialiser method
On button click, I want to create tooltip.
and now I am getting a rectangle.
How do I add a pointer as below image?
Expected:
Current:
Following is the code where I am trying to create tooltip like view
var ToolTipView = UIView()
func tooltip(){
for view in self.ToolTipView.subviews {
view.removeFromSuperview()
}
ToolTipView.removeFromSuperview()
ToolTipView.frame = CGRect(x:50, y:self.view.bounds.height-150, width:100, height:50)
ToolTipView.backgroundColor = UIColor(red:0, green:0, blue:0, alpha:0.7)
ToolTipView.layer.cornerRadius = 5.0
let txtLabel = UILabel(frame: CGRect(x:0, y:2, width:100, height:50))
txtLabel.text = "Click"
txtLabel.textAlignment = NSTextAlignment.Center
txtLabel.textColor = UIColor(red:1, green:1, blue:1, alpha:1)
txtLabel.font = UIFont(name:"HelveticaNeue", size: 16)
self.ToolTipView.addSubview(txtLabel)
self.view.addSubview(ToolTipView)
self.view.bringSubviewToFront(ToolTipView)
}
Try CocoaControls for a list of libraries developed for you. Simply search your query and there will be a list of controls. Chose one of them, and select download source. It will take to Github, download or use pod for that control and enjoy :)
Here is link to your desired query as example.
How about this?
extension UIView {
func displayTooltip(_ message: String, completion: (() -> Void)? = nil) {
let tooltipBottomPadding: CGFloat = 12
let tooltipCornerRadius: CGFloat = 6
let tooltipAlpha: CGFloat = 0.95
let pointerBaseWidth: CGFloat = 14
let pointerHeight: CGFloat = 8
let padding = CGPoint(x: 18, y: 12)
let tooltip = UIView()
let tooltipLabel = UILabel()
tooltipLabel.text = " \(message) "
tooltipLabel.font = UIFont.systemFont(ofSize: 12)
tooltipLabel.contentMode = .center
tooltipLabel.textColor = .white
tooltipLabel.layer.backgroundColor = UIColor(red: 44 / 255, green: 44 / 255, blue: 44 / 255, alpha: 1).cgColor
tooltipLabel.layer.cornerRadius = tooltipCornerRadius
tooltip.addSubview(tooltipLabel)
tooltipLabel.translatesAutoresizingMaskIntoConstraints = false
tooltipLabel.bottomAnchor.constraint(equalTo: tooltip.bottomAnchor, constant: -pointerHeight).isActive = true
tooltipLabel.topAnchor.constraint(equalTo: tooltip.topAnchor).isActive = true
tooltipLabel.leadingAnchor.constraint(equalTo: tooltip.leadingAnchor).isActive = true
tooltipLabel.trailingAnchor.constraint(equalTo: tooltip.trailingAnchor).isActive = true
let labelHeight = message.height(withWidth: .greatestFiniteMagnitude, font: UIFont.systemFont(ofSize: 12)) + padding.y
let labelWidth = message.width(withHeight: .zero, font: UIFont.systemFont(ofSize: 12)) + padding.x
let pointerTip = CGPoint(x: labelWidth / 2, y: labelHeight + pointerHeight)
let pointerBaseLeft = CGPoint(x: labelWidth / 2 - pointerBaseWidth / 2, y: labelHeight)
let pointerBaseRight = CGPoint(x: labelWidth / 2 + pointerBaseWidth / 2, y: labelHeight)
let pointerPath = UIBezierPath()
pointerPath.move(to: pointerBaseLeft)
pointerPath.addLine(to: pointerTip)
pointerPath.addLine(to: pointerBaseRight)
pointerPath.close()
let pointer = CAShapeLayer()
pointer.path = pointerPath.cgPath
pointer.fillColor = UIColor(red: 44 / 255, green: 44 / 255, blue: 44 / 255, alpha: 1).cgColor
tooltip.layer.addSublayer(pointer)
(superview ?? self).addSubview(tooltip)
tooltip.translatesAutoresizingMaskIntoConstraints = false
tooltip.bottomAnchor.constraint(equalTo: topAnchor, constant: -tooltipBottomPadding + pointerHeight).isActive = true
tooltip.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
tooltip.heightAnchor.constraint(equalToConstant: labelHeight + pointerHeight).isActive = true
tooltip.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true
tooltip.alpha = 0
UIView.animate(withDuration: 0.2, animations: {
tooltip.alpha = tooltipAlpha
}, completion: { _ in
UIView.animate(withDuration: 0.5, delay: 0.5, animations: {
tooltip.alpha = 0
}, completion: { _ in
tooltip.removeFromSuperview()
completion?()
})
})
}
}
with String extensions:
extension String {
func width(withHeight constrainedHeight: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: constrainedHeight)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.width)
}
func height(withWidth constrainedWidth: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: constrainedWidth, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.height)
}
}
Usage:
clickMeButton.displayTooltip("Clicked!")
Result:
Dark gray tooltip with text shown over button
class MyViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#objc func expandClicked(sender: UIButton!) {
let vc = UIViewController()
vc.view.backgroundColor = .white
display(viewcontroller: vc, size: CGSize(width: 150, height: 40), sender: sender)
}
func display(viewcontroller: UIViewController, size: CGSize, sender: UIView) {
let controller = viewcontroller
controller.modalPresentationStyle = .popover
controller.popoverPresentationController?.delegate = self
controller.preferredContentSize = size
let presentationController = controller.popoverPresentationController!
presentationController.sourceView = sender
presentationController.sourceRect = sender.bounds
presentationController.backgroundColor = .white
let buttonPosition = CGPoint(x: sender.bounds.minX, y: sender.bounds.maxY)
let p = sender.convert(buttonPosition, to: transactionTableView)
if (transactionTableView.bounds.maxY - p.y) > 70 {
presentationController.permittedArrowDirections = .up
} else {
presentationController.permittedArrowDirections = .down
}
self.present(controller, animated: true)
}
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
}
I have a circle that I'm animating. It works except that the drawing is from the top left.. Can I animate it from the center? If so, any help would be appreciated..
My code for drawing the circle is:
class CircleView: UIView {
override func draw(_ rect: CGRect)
{
let prefs: UserDefaults = UserDefaults.standard
lineWidthFloat = prefs.value(forKey: "lineWidth") as! Float
let circleSize = Double(lineWidthFloat * 100)
let context = UIGraphicsGetCurrentContext()
context!.setLineWidth(10.0)
context!.setFillColor(UIColor.black.cgColor)
let rect = CGRect(x: 20, y: 20, width: circleSize, height: circleSize)
context!.addEllipse(inRect: rect)
context!.fillPath()
}
}
Thanks!
Details
Xcode 10.2.1 (10E1001), Swift 5
Full Sample
CircleView
class CircleView: UIView {
weak var circleView: UIView?
lazy var isAnimating = false
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func setup() {
let rectSide = (frame.size.width > frame.size.height) ? frame.size.height : frame.size.width
let circleRect = CGRect(x: (frame.size.width-rectSide)/2, y: (frame.size.height-rectSide)/2, width: rectSide, height: rectSide)
let circleView = UIView(frame: circleRect)
circleView.backgroundColor = UIColor.yellow
circleView.layer.cornerRadius = rectSide/2
circleView.layer.borderWidth = 2.0
circleView.layer.borderColor = UIColor.red.cgColor
addSubview(circleView)
self.circleView = circleView
}
func resizeCircle (summand: CGFloat) {
guard let circleView = circleView else { return }
frame.origin.x -= summand/2
frame.origin.y -= summand/2
frame.size.height += summand
frame.size.width += summand
circleView.frame.size.height += summand
circleView.frame.size.width += summand
}
private func animateChangingCornerRadius (toValue: Any?, duration: TimeInterval) {
guard let circleView = circleView else { return }
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.fromValue = circleView.layer.cornerRadius
animation.toValue = toValue
animation.duration = duration
circleView.layer.cornerRadius = circleView.frame.size.width/2
circleView.layer.add(animation, forKey:"cornerRadius")
}
private func circlePulseAinmation(_ summand: CGFloat, duration: TimeInterval, completionBlock:#escaping ()->()) {
guard let circleView = circleView else { return }
UIView.animate(withDuration: duration, delay: 0, options: .curveEaseInOut, animations: { [weak self] in
self?.resizeCircle(summand: summand)
}) { _ in completionBlock() }
animateChangingCornerRadius(toValue: circleView.frame.size.width/2, duration: duration)
}
func resizeCircleWithPulseAinmation(_ summand: CGFloat, duration: TimeInterval) {
if (!isAnimating) {
isAnimating = true
circlePulseAinmation(summand, duration:duration) { [weak self] in
guard let self = self else { return }
self.circlePulseAinmation((-1)*summand, duration:duration) {self.isAnimating = false}
}
}
}
}
ViewController
import UIKit
class ViewController: UIViewController {
weak var circleView: CircleView?
weak var button: UIButton?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let circleView = CircleView(frame: CGRect(x: 40, y: 50, width: 40, height: 60))
circleView.backgroundColor = UIColor.clear
view.addSubview(circleView)
self.circleView = circleView
let button = UIButton(frame: CGRect(x: 20, y: 150, width: 80, height: 40))
button.setTitle("Animate", for: UIControl.State())
button.setTitleColor(UIColor.blue, for: UIControl.State())
button.setTitleColor(UIColor.blue.withAlphaComponent(0.3), for: .highlighted)
button.addTarget(self, action: #selector(ViewController.animateCircle), for: .touchUpInside)
view.addSubview(button)
self.button = button
}
#objc func animateCircle() {
circleView?.resizeCircleWithPulseAinmation(30, duration: 1.5)
}
}
Result
You should be able to do it a lot simpler using the transform property.
func pulse() {
UIView.animate(withDuration: 0.5, animations:{
self.circleView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}, completion: { _ in
UIView.animate(withDuration: 0.5, animations: {
self.circleView.transform = .identity
})
})
}