I have a UIViewController subclass that contains a UITableView, a subclass of UIView called ICYCollapsableInputView, and another UIView subclass called ICYHeaderVIew.
ICYHeaderView is the rounded off looking UIView shown in the screenshots. It contains a round button - the one with the green "add" (+) button.
ICYCollapsableInputView is a view that will for now have a UITextField and a Save button. I want to be able to expand and collapse this view from the UIViewController by calling the appropriate functions of the ICYCollapsableInputView.
When the user taps the round button on the ICYHeaderView, Ithe ICYCollapsableInputView is supposed to expand in height, pushing down the UITableView. I have the ICYCollapsableInputView expanding and collapsing, but I can't figure out how to get the UITableView to respond to the changes. (See Animation)
I colored the main view in the xib with a RED background and the view I added in it with a BLUE background (see screenshot)
I can resize the ICYCollapsableInputView (as seen in the code and animation) using its own height constraint, but the UITableView constraint seems to ignore it.
Here is the code from ICYCollapsableInputView:
class ICYCollapsableInputView: UIView {
var view: UIView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBInspectable public var collapsedHeight: CGFloat = 150 {
didSet {
self.heightConstraint.constant = collapsedHeight
}
}
#IBInspectable public var expandedHeight: CGFloat = 250 {
didSet {
self.heightConstraint.constant = expandedHeight
}
}
// MARK: - Init
func loadFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "ICYCollapsableInputView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
override init(frame: CGRect) {
// 1. setup any properties here
// 2. call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
// 1. setup any properties here
// 2. call super.init(coder:)
super.init(coder: aDecoder)
// 3. Setup view from .xib file
xibSetup()
}
func xibSetup() {
view = loadFromNib()
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
self.heightConstraint.constant = self.collapsedHeight
var rect = self.frame
rect.size.height = self.collapsedHeight
self.frame = rect
self.view.frame = rect
}
func revealInputView() {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
var rect = self.frame
rect.size.height = self.expandedHeight
self.frame = rect
self.heightConstraint.constant = self.expandedHeight
self.view.layoutIfNeeded()
}, completion: nil)
}
func closeInputView() {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
self.heightConstraint.constant = self.collapsedHeight
var rect = self.frame
rect.size.height = self.collapsedHeight
self.frame = rect
self.view.layoutIfNeeded()
}, completion: nil)
}
}
All you need ia just changing the height constraints.
func revealInputView() {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
self.heightConstraint.constant = self.expandedHeight //or 250
self.view.layoutIfNeeded()
}, completion: nil)
}
func closeInputView() {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
self.heightConstraint.constant = self.collapsedHeight //or 150
self.view.layoutIfNeeded()
}, completion: nil)
}
And Constraints in Storyboard should be following
for inputVW
Trailaing space to superview - 0 , leading space to superview - 0, top space to superview - 0 , bottom space to tableview - 0
for TableView
top space to inputVw - 0, leading space to superview - 0, trailing space to super view - 0, bottom space to superview - 0
Related
I have a custom UIView class that creates a square with a red background and I'm trying to get a green square slide on top of it from the left. I have a button from the main view that brings me to the screen above but the green screen doesn't animate over it. It just appears at the end result. I do know that I am creating these shapes when I change the initial width of the green square but no indication of an animation.
MainViewController.swift
private func configureAnimatedButton() {
//let sampleAnimatedButton
let sampleAnimatedButton = AnimatedButtonView()
sampleAnimatedButton.center = CGPoint(x: self.view.frame.size.width / 2,
y: self.view.frame.size.height / 2)
self.view.addSubview(sampleAnimatedButton)
sampleAnimatedButton.animateMiddleLayer()
}
AnimatedButton.swift
import Foundation
import UIKit
public class AnimatedButtonView: UIView {
//initWithFrame to init view from code
let movingLayer = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
//initWithCode to init view from xib or storyboard
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
//common func to init our view
private func setupView() {
//backgroundColor = .red
self.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
self.backgroundColor = .red
movingLayer.frame = CGRect(x: self.frame.maxX, y: self.frame.minY, width: 0, height: 300)
movingLayer.backgroundColor = .green
self.addSubview(movingLayer)
}
public func animateMiddleLayer() {
UIView.animate(withDuration: 10.0, delay: 1.2, options: .curveEaseOut, animations: {
self.layoutIfNeeded()
var grayLayerTopFrame = self.movingLayer.frame
grayLayerTopFrame.origin.x = 0
grayLayerTopFrame.size.width += 300
self.movingLayer.frame = grayLayerTopFrame
self.layoutIfNeeded()
}, completion: { finished in
print("animated")
})
}
}
You should move grayLayerTopFrame outside the UIView.animate(), before it starts. Also, you don't need layoutIfNeeded if you're just animating the frame (you use it when you have constraints).
var grayLayerTopFrame = self.movingLayer.frame
grayLayerTopFrame.origin.x = 0
grayLayerTopFrame.size.width += 300
UIView.animate(withDuration: 10.0, delay: 1.2, options: .curveEaseOut, animations: {
self.layoutIfNeeded()
self.movingLayer.frame = grayLayerTopFrame
self.layoutIfNeeded()
}, completion: { finished in
print("animated")
})
Also, make sure you don't call it in viewDidLoad() -- at that time, the views aren't laid out yet.
I have an image of what symbolises a heartbeat
and I'm trying to loop it in my subview so it continuously appears on my view if you understand what i mean. So far I have got it to appear and move across infinitely on my subview however it isn't continuous as it only appears again from the left after it has completely gone out of the view on the right where as I want it to appear again as soon as it has completely entered the view so it symbolises an actual heartbeat. My code looks something like this on my viewDidLoad:
self.heartbeatLoading.frame.origin = CGPoint(x: 0 - heartbeatLoading.frame.size.width, y: 10)
let animation: UIViewAnimationOptions = [.repeat, .curveLinear]
UIView.animate(withDuration: 5.0, delay: 0, options: animation, animations: {
self.heartbeatLoading.frame = self.heartbeatLoading.frame.offsetBy(dx: self.view.frame.width + self.heartbeatLoading.frame.width, dy: 0.0)
}, completion: nil)
Take one UIView and set this class as a super view. Call start animation and stop the animation whenever required.
#IBDesignable class Pulse : UIView
{
var animatingView : UIView?
#IBInspectable var pulseImage: UIImage = UIImage(named: "defaultImage")!{
didSet {
animatingView = UIView(frame: self.bounds)
let imgView = UIImageView(frame : self.bounds)
animatingView?.clipsToBounds = true
animatingView?.addSubview(imgView)
self.addSubview(animatingView!)
}
}
func startAnimationWith(duration : Double = 0.5)
{
if animatingView != nil
{
animatingView?.frame = CGRect(x: 0, y: 0, width: 0, height: (animatingView?.frame.size.height)!)
UIView.animate(withDuration: duration, delay: 0, options: .repeat, animations: {
self.animatingView?.frame = CGRect(x: 0, y: 0, width: (self.animatingView?.frame.size.width)!, height: (self.animatingView?.frame.size.height)!)
}, completion: { (isDone) in
})
}
}
func stopAnimation()
{
if animatingView != nil
{
self.animatingView!.layer.removeAllAnimations()
}
}
override init(frame : CGRect) {
super.init(frame : frame)
}
convenience init() {
self.init(frame:CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Inside a UIViewController, I call a .xib file and present it over the current UIView.
// initiate the pop up ad view and display it
let popUpAdView = PopUpAdViewController(nibName: "PopUpAdView", bundle: nil)
popUpAdView.displayIntoSuperView(view)
There is a button inside that .xib file that should remove itself from the screen when it's touched. However it doesn't perform so.
What exactly am I missing?
class PopUpAdViewController: UIViewController {
#IBOutlet weak var popUpAdView: UIView!
#IBOutlet weak var closeButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// adjust the view size from the device's screen size
let screenSize = UIScreen.mainScreen().bounds
view.frame = CGRectMake(0, 64, screenSize.width, screenSize.height-64)
// cosmetically adjust the view itself
view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.6)
view.userInteractionEnabled = true
closeButton.userInteractionEnabled = true
// style the pop up ad view
popUpAdView.layer.cornerRadius = 5
popUpAdView.layer.shadowOpacity = 0.8
popUpAdView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
closeButton.addTarget(self, action: #selector(removeOutOfSuperView), forControlEvents: .TouchUpInside)
}
func displayIntoSuperView(superView: UIView!) {
superView.addSubview(self.view)
// define the initial cosmetical values of the items
popUpAdView.transform = CGAffineTransformMakeScale(0.3, 0.3)
popUpAdView.alpha = 0.0
closeButton.alpha = 0.0
// animate...
UIView.animateWithDuration(0.8, delay: 0.0, options: .CurveEaseIn, animations: {
self.popUpAdView.alpha = 1.0
self.popUpAdView.transform = CGAffineTransformMakeScale(1.0, 1.0)
}) { (Bool) in
UIView.animateWithDuration(0.6, delay: 1.5, options: .CurveEaseIn, animations: {
self.closeButton.alpha = 1.0
}, completion: { (Bool) in
})
}
}
func removeOutOfSuperView() {
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseIn, animations: {
self.closeButton.alpha = 0.0
self.popUpAdView.transform = CGAffineTransformMakeScale(0.1, 0.1)
}) { (finished) in
UIView.animateWithDuration(0.8, delay: 0.0, options: .CurveEaseIn, animations: {
self.view.alpha = 0.0
self.view.removeFromSuperview()
}, completion: nil)
}
}
#IBAction func closePopUpAdView(sender: AnyObject) {
print("Closing the pop up ad...")
removeOutOfSuperView()
}
}
Update
.xib structureL:
Im building a sample where i can post comments where i am using tableView to add a cell each time i post. its mostly handled but I'm suffering of one issue when i want to post a comment, the tableview bottom is the same as keyboard height but empty and if place it zero the view will move down then pushed top, and thats because I'm moving the whole view when i click on textview to write text.
This is a preview of the empty space under the tableView (keyboard height as inset) : Preview of image on Google Drive
Another image to see if i made the bottom (content inset) = 0 when keyboard is opening : Preview of second image on Google Drive
func keyboardWillShow(sender:NSNotification){
let userInfo: [NSObject : AnyObject] = sender.userInfo!
keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
var contentInsets = UIEdgeInsets()
if wasEmoj == true {
if (UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation)) {
contentInsets = UIEdgeInsetsMake(257.0, 0.0, (self.keyboardSize.height), 0.0);
} else {
contentInsets = UIEdgeInsetsMake(257.0, 0.0, (self.keyboardSize.width), 0.0);
}
self.mytableView.contentInset = contentInsets;
self.mytableView.scrollIndicatorInsets = contentInsets
}else {
if (UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation)) {
contentInsets = UIEdgeInsetsMake(215.0, 0.0,(self.keyboardSize.height), 0.0);
} else {
contentInsets = UIEdgeInsetsMake(215.0, 0.0, (self.keyboardSize.width), 0.0);
}
self.mytableView.contentInset = contentInsets;
self.mytableView.scrollIndicatorInsets = contentInsets
}
}
func textViewDidBeginEditing(textView: UITextView) {
self.animateTextField(commentText, up: true, movementDistance: -215, duration: 0.3)
}
///- animate the view up or down to adapt with keyboard
func animateTextField (textField:UITextView,up:Bool,movementDistance:Int,duration:NSTimeInterval){
let movement : Int = (up ? movementDistance : -movementDistance);
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
if up == true {
UIView.setAnimationDuration(0.38)
}else {
UIView.setAnimationDuration(0.3)
}
self.view.frame = CGRectOffset(self.view.frame, 0, CGFloat(movement))
UIView.commitAnimations()
}
It's better to change the frame of the tableview in this case.
You can keep the instance of the constraint between the commentView's bottom and the bottom layout guide.
#IBOutlet weak var commentViewBottom: NSLayoutConstraint!
Then change the constant of the constraint when needed.
func keyboardWillShow(sender:NSNotification){
if let dic = sender.userInfo {
if let keyboardFrame = dic[UIKeyboardFrameEndUserInfoKey]?.CGRectValue {
if let duration = dic[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue {
commentViewBottom.constant = -keyboardFrame.height
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .CurveLinear, animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
}
func keyboardWillHide(sender: NSNotification) {
if let dic = sender.userInfo, let duration = dic[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue {
commentViewBottom.constant = 0
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .CurveLinear, animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
}
i'm working on a UIButton animation where it moves along x-axis while it scales to its original size from the initial size. when i tried the code below it doesnt move to the point where it should go and doesn't scale up to its original size.
this is my code for initializing the button:
_testBtn1.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
scaleBtn = CGAffineTransformMakeScale(0.2, 0.2);
[_testBtn1 setTransform: scaleBtn];
and this is my code for the moving/translation and scaling:
CGAffineTransform translate = CGAffineTransformMakeTranslation(50.0f, 0.0f);
CGAffineTransform animate = CGAffineTransformConcat(scaleBtn, translate);
[_testBtn1 setTransform:animate];
any help, suggestion, advice will be appreciated. i'm new to iOS..thanks!
You can just create a custom type UIButton (either with IB by changing the type to Custom, or programmatically with
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeCustom];//set the button's image with
[aButton setImage:[UIImage imageNamed:#"abc" forState:UIControlStateNormal];
To move it, just animate its position normally...
[UIView animateWithDuration:0.5 animations:^{aButton.center = newCenter;}];
Or
CGRect originalFrame = aButton.frame;
aButton.frame = CGRectMake(originalFrame.origin.x, originalFrame.origin.y, originalFrame.size.width, 0);
[UIView animateWithDuration:0.5 animations:^{aButton.frame = originalFrame;}];
OR Reffer this link
http://objectiveapple.blogspot.in/2011/10/23-quizapp-16-animate-scale-uibutton.html
This should be easily done with Core Animation, take a look the the most basic one CABasicAnimation
A simple scale(bouncy) animation with completion handler implementation in swift.
Hope it'll help you or anyone else in some way.
viewToanimate.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 0.7,
delay: 0,
usingSpringWithDamping: 0.2,
initialSpringVelocity: 6.0,
animations: { _ in
viewToAnimate.transform = .identity
},
completion: { _ in
//Implement your awesome logic here.
})
I made this animation:
With this reusable class:
class AnimatedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
addTargets()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
addTargets()
}
func addTargets() {
self.addTarget(self, action: #selector(scaleDownAnimated), for: .touchDown)
self.addTarget(self, action: #selector(scaleBackToNormalAnimated), for: [.touchUpOutside, .touchCancel, .touchUpInside])
}
#objc func scaleDownAnimated() {
UIView.animate(withDuration: 0.25, delay: 0, options: .allowUserInteraction) {
self.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
} completion: { _ in }
}
#objc func scaleBackToNormalAnimated() {
UIView.animate(withDuration: 0.2, delay: 0, options: .allowUserInteraction) {
self.transform = CGAffineTransform(scaleX: 1, y: 1)
} completion: { _ in }
}
}
Then just make the class of your button AnimatedButton like below (if you are using Storyboards) and use the button normally: