How to redraw a subviews in landscape - swift

Good afternoon Community,
I am trying to add two subviews to a viewcontroller by means of an extension, but when rotating the device it does not respect the layout, any option on how to make it look correct?
I attach my code below and a couple of example photos, the first photo is how I would like it to look, and the rest of the ui is lost.
This is my code:
func presentCustomAlertController(on ViewController:UIViewController,toPresentCustomView customView:UIView,withBackgroundView backGroundView:UIView){
customView.translatesAutoresizingMaskIntoConstraints = false
guard let targetView = ViewController.view else {return}
backGroundView.alpha = 0
backGroundView.backgroundColor = .black
backGroundView.frame = targetView.bounds
targetView.addSubview(backGroundView)
targetView.addSubview(customView)
customView.setNeedsLayout()
customView.setNeedsDisplay()
customView.layoutIfNeeded()
customView.layoutSubviews()
targetView.setNeedsLayout()
targetView.setNeedsDisplay()
targetView.layoutIfNeeded()
targetView.layoutSubviews()
backGroundView.setNeedsDisplay()
backGroundView.setNeedsLayout()
backGroundView.layoutIfNeeded()
customView.frame = CGRect(x: targetView.center.x, y: targetView.center.y, width: targetView.frame.size.width-80, height: 300)
UIView.animate(withDuration: 0.25, animations: {
backGroundView.alpha = 0.6
},completion: { done in
UIView.animate(withDuration: 0.25, animations: {
customView.center = targetView.center
})
})
}
}

Related

Swift UIView zPosition issue, views overlap on other views

I am writing a calendar view, I added three other views to its super view, so the calendar view should be on the topper layer, it looks fine on the simulator
but later on, I found that I can't click the button, then I checked the view hierarchy, found that the other three views overlap on the calendar, it is so weird as the 3d preview and view on the simulator are different
Here is the code of UI Part
func updateUI() {
if calendarView != nil {
self.view.addSubview(calendarView!)
self.calendarView!.frame.origin = calendarViewPosition!
self.calendarView!.layer.zPosition = 5
self.calendarView!.layer.cornerRadius = self.setting.itemCardCornerRadius
self.calendarView!.layer.masksToBounds = true
self.calendarView!.setViewShadow()
UIView.animate(withDuration: 0.8, delay: 0, options: .curveEaseOut, animations: {
self.calendarView!.frame.origin.y += 50
}) { _ in
var newCalendarPageCordiateYDifference: CGFloat = 10
var newCalendarPageSizeDifference: CGFloat = 20
var zPosition: CGFloat = 4
for _ in 1 ... 3 {
let newCalendarPage = UIView()
newCalendarPage.frame = CGRect(x: self.calendarView!.frame.origin.x + newCalendarPageSizeDifference / 2, y: self.calendarView!.frame.origin.y, width: self.calendarView!.frame.width - newCalendarPageSizeDifference, height: self.calendarView!.frame.height - newCalendarPageSizeDifference)
newCalendarPage.backgroundColor = .white
newCalendarPage.layer.zPosition = zPosition
newCalendarPage.layer.cornerRadius = self.setting.itemCardCornerRadius
newCalendarPage.setViewShadow()
self.view.addSubview(newCalendarPage)
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
newCalendarPage.frame.origin.y -= newCalendarPageCordiateYDifference
})
zPosition -= 1
newCalendarPageCordiateYDifference += 10
newCalendarPageSizeDifference += 50
}
}
}
}
Remove self.view.addSubview(newCalendarPage) this from for loop and
Add subview like this
self.view.insertSubview(newCalendarPage, belowSubview: calendarView)
Another way is to disable user interaction like
newCalendarPage.isUserInteractionEnabled = false

Toast UILabel appears off screen, despite me setting y correctly

I have a Toast extension on a ViewController that inherits class ViewControllerList: UITableViewController, UIPopoverPresentationControllerDelegate { and everything is fine except that when I do this
extension UIViewController {
func showToast(message : String) {
let height = UIScreen.main.bounds.height
let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 75, y: height-100, width: 150, height: 35))
toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = .center;
toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
self.view.addSubview(toastLabel)
UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
toastLabel.alpha = 0.0
}, completion: {(isCompleted) in
toastLabel.removeFromSuperview()
})
}
}
the toast ALWAYS appears off screen .. in a long uitableview .. no matter how I set the y it always thinks in terms of document.. I have debugged it and y=676 yet it appears about placement at around ~900 ...100 off the bottom of the tableView just inside the last cell
why is this and how do I fix it?
Please do not mark this down - TRY to give an answer
Instead of adding to self.view, add your toast label as a subview of the window:
extension UIViewController {
func showToast(message : String) {
let window = (UIApplication.shared.delegate as! AppDelegate).window
let height = window.bounds.height
let toastLabel = UILabel(frame: CGRect(x: window.bounds.width/2 - 75, y: height-100, width: 150, height: 35))
toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = .center;
toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
// notice the change here
window.addSubview(toastLabel)
UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
toastLabel.alpha = 0.0
}, completion: {(isCompleted) in
toastLabel.removeFromSuperview()
})
}
}
This avoids the problems you have to deal with when the super view is a scroll view.
Add a new function addToTopView(view : UIView) and use like here.
extension UIViewController {
/// adding views as a subview of the top view = window
func addToTopView(view : UIView) {
if let d = UIApplication.shared.delegate, let window = d.window! {
window.addSubview(view)
// handle constraints or anythings
}
}
func showToast(message : String) {
let height = UIScreen.main.bounds.height
let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 75, y: height-100, width: 150, height: 35))
toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = .center;
toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
addToTopView(view: toastLabel) // here is update
UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
toastLabel.alpha = 0.0
}, completion: {(isCompleted) in
toastLabel.removeFromSuperview()
})
}
}

Swift: Fading two images while chaging frame

Can someone tell me how can I animate a cross dissolve transition while chaging the initial frame?
My code:
self.image = initialImage
UIView.transition(with: _self, duration: 10.0, options: [.transitionCrossDissolve, .allowUserInteraction], animations: {
self.image = newImage
})
Also tried:
let transition = CATransition()
transition.duration = 10
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
transition.delegate = self
self?.layer.add(transition, forKey: nil)
Changing the frame size while the animation runs leaves the initial image at the initial frame size, while the newImage adapts to the updated frame.
Thank you.
For an easier approach, create two image view instances and animate the alpha of the first one, while changing the frame for both of them.
let firstImageView = UIImageView(image: UIImage(named: "first"))
firstImageView.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
let secondImageView = UIImageView(image: UIImage(named: "second"))
secondImageView.frame = firstImageView.frame
//the second image view should obscure the first one, that's why it's the first in the array
let imageViews = [secondImageView, firstImageView]
imageViews.forEach { view.addSubview($0) }
let newFrame = CGRect(x: 50, y: 50, width: 100, height: 100)
UIView.animate(withDuration: 10) {
firstImageView.alpha = 0
imageViews.forEach { $0.frame = newFrame }
}

Animation lags on device, but not in Simulator

I want to trigger a CAAnimation with a Button. In playground and in the simulator, this works exactly as I want it to. However, when I run the same code on a device, the animation happens only after a short delay.
Apparently, the issue only happens on iOS 11.2.6. I updated my device and can now not reproduce the issue anymore. Can anyone confirm, or find out, how it would work on iOS 11.2.6?
import UIKit
class MyViewController : UIViewController {
let animatedView = UIView()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
// Add a button
let button = UIButton(type: .system)
button.frame = CGRect(x: 150, y: 200, width: 200, height: 50)
button.setTitle("Animate", for: .normal)
button.addTarget(self, action: #selector(tap), for: .touchUpInside)
// Set color and frame of the view, that is animated.
animatedView.backgroundColor = UIColor.blue
animatedView.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
// Add the views to the view hierarchy
view.addSubview(animatedView)
view.addSubview(button)
self.view = view
}
/// On Tap create an animation, that changes the position of the animated view.
#objc func tap() {
let originalY = animatedView.layer.position.y
let animation = CABasicAnimation(keyPath: "position.y")
animation.fromValue = originalY
animation.toValue = 300.0
animation.duration = 1.0
animatedView.layer.add(animation, forKey: "positionAnimation")
}
}
Can you try this code with iOS 11.2.9, I have tested your code and it's working fine.
// MARK: Animations
public extension CALayer {
var ani: CAAnimation {
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = CGPoint(x: 0, y: 0.0)
startPointAnim.toValue = CGPoint(x:0, y: 300)
let endPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.endPoint))
endPointAnim.fromValue = CGPoint(x: 0, y: 0)
endPointAnim.toValue = CGPoint(x:2, y: 300)
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = 1.0
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
return animGroup
}
func playAnimation(_ anim: SkeletonLayerAnimation, key: String) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { add(anim(self), forKey: key) }) {
$0.playAnimation(anim, key: key)
}
}
func stopAnimation(forKey key: String) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { removeAnimation(forKey: key) }) {
$0.stopAnimation(forKey: key)
}
}
}

Word by Word UILabel animation from left to right in swift

I have a uilabel with a text suppose " This is a label." I want this label to be displayed one word at a time flying from outside screen to the UILable position..
something like
label.
a label.
is a label.
This is a label.
How can i get such animation
I have found a way to do as i wished.
class ViewController: UIViewController {
#IBOutlet var sampleLabel: UILabel!
var slogan = "This is a slogan."
var xdir = 250
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let sampleLabelFrame = sampleLabel.frame
let ypos = sampleLabelFrame.origin.y
var sloganArray = slogan.componentsSeparatedByString(" ")
sloganArray = sloganArray.reverse()
var i = 0.0
for word in sloganArray{
let label: UILabel = UILabel(frame: CGRect(x: -100, y:ypos, width: 60, height: 20))
label.text = word
view.addSubview(label)
let width = label.intrinsicContentSize().width
var labelFramewidth = label.frame
labelFramewidth.size.width = width
label.frame = labelFramewidth
self.xdir = self.xdir - Int(width)-4
UIView.animateWithDuration(0.7, delay: i, options: .CurveEaseOut, animations: {
var labelframe = label.frame
labelframe.origin.x = CGFloat(self.xdir)
label.frame = labelframe
}, completion: { finished in
})
i+=0.5
}
}
}
hope this helps others in need of something like this.
This might help you on the way to achieve what you want:
http://helpmecodeswift.com/animation/creating-animated-labels
EDIT:
Flying label code:
meme1.text = "Brace yourself!"
meme1.font = UIFont.systemFontOfSize(25)
meme1.textColor = UIColor.whiteColor() // This is all just styling the Label
meme1.sizeToFit()
meme1.center = CGPoint(x: 200, y: -50) // Starting position of the label
view.addSubview(meme1) // Important! Adding the Label to the Subview, so we can actually see it.
//Animation options. Play around with it.
UIView.animateWithDuration(0.9, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options: .CurveLinear, animations: {
self.meme1.center = CGPoint(x: 200, y:50 ) // Ending position of the Label
}, completion: nil)
UIView.animateWithDuration(0.6, delay: 1.1, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options: .CurveLinear, animations: {
self.meme1.center = CGPoint(x: 200, y:150+90 )
}, completion: nil)
I have no experience with it myself, but it seems to get your job done. You can eventually se the starting point outside the screen and let it animate over to the screen. That should give the desired effect.