How to properly animate SCNView height? - ios

I'm trying to animate SCNView height, but it seems like there are some visual issue.
When content is growing it looks like everything is ok, but when I'm trying to decrease the height, view just jumps to final height instantly.
let newHeight = scene.frame.height + (open ? 100.0 : -100.0)
UIView.animate(withDuration: 1.0, animations: {
self.scene.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: newHeight)
}) { finished in
self.open = !self.open
}
See
Code Running in Sumulator
With regular view everything works fine.

Set contentMode of SCNView to .scaleToFill where needed (e.g. in viewDidLoad). It works as expected.
let view: SCNView
...
view.contentMode = .scaleToFill

There is a relationship between this behavior and the content mode of scene. You may try:
scene.contentMode = .scaleToFill

Related

UI freezes for a second when returning to the previous ViewController in Navigation Stack

I'm having some trouble figuring out what's causing the UI to freeze for a second when I press the back button. It started happening after I added a background image to the viewController that I'm transitioning from. If I'm just using "white" as my backgroundColor, the transition doesn't freeze, it only freezes once I add the image.
Here is a gif of what it looks like...
https://gfycat.com/waryagileichidna
Here is the extension that I'm calling in my viewDidLoad to set the background image...
extension UIView {
func addBackground(image:String) {
self.backgroundColor = .white
// screen width and height:
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
let imageViewBackground = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
imageViewBackground.image = UIImage(named: "\(image)")
imageViewBackground.alpha = 0.5
// you can change the content mode:
imageViewBackground.contentMode = UIView.ContentMode.scaleAspectFill
self.addSubview(imageViewBackground)
self.sendSubviewToBack(imageViewBackground)
}
}
Could you try adding the below line to your addBackground method which you call in viewDidLoad.
imageViewBackground.clipsToBounds = true
I have a feeling this should solve it. That doesn't look like a freeze.

UIScrollView pages content not working properly - iOS

I've been trying for one whole day to make it work right. I tried different tutos and made adjustments (searching in here) but it doesnt work properly.
On iphone 5s, it almost works right, excepts for the last page that it seems to add "half" of a page on scroll view. The green part is the background color of the scroll view and it doesn't bounce back.
On iphone 6, the content is all messed up and instead of having 5 images, it has only 4 and a half (i cant scroll it anymore and it doesn't bounce back).
If I press continue, it works perfectly on both screen sizes. It is only the swipe option that is not working properly.
Here is the code where I put the images inside the scroll view.
func loadContent() {
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
for i in 0..<imageArr.count { // is 5
frame.origin.x = scrollView.frame.size.width * CGFloat(i)
frame.size = self.scrollView.frame.size
let imageView = UIImageView(frame: frame)
let image = UIImage(named: imageArr[i])
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.backgroundColor = UIColor.red
imageView.image = image
scrollView.addSubview(imageView)
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i)
}
}
///Function of Continue button
#IBAction func nextImage(_ sender: Any) {
UIView.animate(withDuration: 0.3) {
self.scrollView.contentOffset.x = self.scrollView.frame.width * CGFloat(self.pageControl.currentPage+1)
}
}
I've been searching for 4 hours for a solution but nothing seems to work.
[Edit]: With the answer of TheFuquan I could solve the problem.
[Obs:] if you searching about the messed content, these lines of code can help you. All the tutos that I saw didn't have these lines and the content just wouldnt align perfect and this solved:
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
Why is the following line in your for loop:
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i)
You should do this before the for loop like this:
self.scrollView.contentSize.width = self.scrollView.frame.width * imageArr.count
this is a typical problem that i face each time i try to load some dynamic content inside a view.
If you re calling your function loadContent from viewDidLoad, then you should call your loadContent inside viewDidLayoutSubviews
Be aware that unlike viewDidLoad, viewDidLayoutSubviews may get called multiple times, so have a flag to ensure that your loadContent gets invoked only once.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard self.didLoadContent == false else {
return
}
self.didLoadContent = true
loadContent()
}
this way you let the view loaded from your storyboard to resize the iPhone's size.

UIView.animate problems with view frame

I'm struggling trying to make an animation. This is what's happening: video
The back card was supposed to do the same animation as in the beginning of the video, but it's doing a completely different thing. I'm checking the UIView.frame at the beginning of the animation and it's the same as the first time the card enters, but obviously something is wrong... Here is the code:
func cardIn() {
let xPosition = (self.darkCardView?.frame.origin.x)! - 300
let yPosition = (self.darkCardView?.frame.origin.y)!
self.initialPos = self.darkCardView.frame
let height = self.darkCardView?.frame.size.height
let width = self.darkCardView?.frame.size.width
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(Double.pi/4)))!
UIView.animate(withDuration: 1.1, animations: {
self.darkCardView?.frame = CGRect(x: xPosition, y: yPosition, width: width!, height: height!)
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
})
}
func cardOut() {
let xPosition = (self.darkCardView?.frame.origin.x)! - 600
let yPosition = (self.darkCardView?.frame.origin.y)!
let height = self.darkCardView?.frame.size.height
let width = self.darkCardView?.frame.size.width
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
UIView.animate(withDuration: 1.0, delay: 0, options: .allowAnimatedContent, animations: {
self.darkCardView?.frame = CGRect(x: xPosition, y: yPosition, width: width!, height: height!)
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(-Double.pi/4)))!
}) { (true) in
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
self.darkCardView?.frame = self.initialPos
self.cardIn()
}
}
Does somebody know how can I repeat the same animation that's in the beginning of the video after cardOut function is called?
Given that we do not know where/when you are calling the above two functions I can only take a guess at what is happening.
So you have an animate in and an animate out, but what about a reset function?
Timeline:
Animate the card in.
...
Eventually animate the card out
...
Reset the cards location to off screen to the right (location before animating in for the first time)
...
animate the card in
...
Repeat, ect....
I managed to solve the animation problem by calling viewDidLoad() instead of calling self.cardIn() in the end o cardOut completion block. But I don't want to depend on calling viewDidLoad every time a new card has to enter the screen...
Also, I didn't set any properties of the back card in viewDidLoad. I only have it in the .xib file. Any ideas?
Note that if you set a view's transform to anything other than the identity transform then the view's frame rect becomes "undefined". You can neither set it to a new value nor read it's value reliably.
You should change your code to use the view's center property if you're changing the transform.
As Jerland points out in their post, you also probably need to reset your back card to it's starting position before beginning the next animation cycle. (Set it to hidden, put it's center back to the starting position and set it's transform back to the identity transform.
EDIT:
This code:
UIView.animate(withDuration: 1.0, delay: 0, options: .allowAnimatedContent, animations: {
self.darkCardView?.frame = CGRect(x: xPosition, y: yPosition, width: width!, height: height!)
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(-Double.pi/4)))!
}) { (true) in
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
self.darkCardView?.frame = self.initialPos
self.cardIn()
}
Does not set the transform to identity. Try changing that line to
self.darkCardView?.transform = .identity

getting highly confused by odd behaviour of viewController Method

I have overrided a method as
override func viewDidLayoutSubviews() {
// creating bottom line for textField
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.whiteColor().CGColor
border.frame = CGRect(x: 0, y: emailTextField.frame.size.height - width, width: emailTextField.frame.size.width, height: emailTextField.frame.size.height)
border.borderWidth = width
emailTextField.layer.addSublayer(border)
emailTextField.layer.masksToBounds = true
}
Now whats happening is that when I run my app on Iphone 6, 6+ every thing works fine. But when I run the same code on iphone5 (Simulator + real Device ) viewDidLayoutSubViews is getting called infinite times and my app becoms unresponsive. I solved the problem by using a bool variable. But I do not understand why is this happening. So can someone please explain this to me.
Thanks :-)
As docs says that viewDidLayoutSubviews method need for change layout
Called to notify the view controller that its view has just laid out
its subviews. When the bounds change for a view controller's view,
the view adjusts the positions of its subviews and then the system
calls this method. However, this method being called does not indicate
that the individual layouts of the view's subviews have been
adjusted. Each subview is responsible for adjusting its own layout.
Move your code to viewDidLoad method or to loadView if you are not using xibs or Storyboards
override func viewDidLoad() {
// creating bottom line for textField
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.whiteColor().CGColor
border.frame = CGRect(x: 0, y: emailTextField.frame.size.height - width, width: emailTextField.frame.size.width, height: emailTextField.frame.size.height)
border.borderWidth = width
emailTextField.layer.addSublayer(border)
emailTextField.layer.masksToBounds = true
}
If you would like to change border.frame you can set it to class variable and change it in viewDidLayoutSubviews method
override func viewDidLayoutSubviews() {
self.border.frame = CGRect(x: 0, y: emailTextField.frame.size.height - width, width: emailTextField.frame.size.width, height: emailTextField.frame.size.height)
self.border.borderWidth = width
}
Just replace
emailTextField.layer.addSublayer(border)
with this:
emailTextField.addSublayer(border)
-> you want to add sublayer to a view - as you want to apply masksToBounds property to it either.
Move your implementation inside viewDidLayoutSubviews. I got the same issue. Hope it will work.
override func viewDidLayoutSubviews()
{
//Your CODE
}

Setting large images in UIScrollView

I want to put images in UIScrollView. However, the problem is that when I try to put an image larger than UIScrollView, UIScrollView shows the upper part of images. I would like the scrollview to show the bottom part of the image. Right now, I have the following code:
let imgBot1 = UIImage(named:"Img1.jpg");
let imgBot2 = UIImage(named:"Img4.jpg");
let imgBot3 = UIImage(named:"Img5.jpg");
//Adding UIImage in UIImageView
let imgView1 = UIImageView(image:imgBot1)
let imgView2 = UIImageView(image:imgBot2)
let imgView3 = UIImageView(image:imgBot3)
//creating UIScrollView
let scrView2 = UIScrollView()
scrView2.frame = CGRectMake(0, self.view.frame.height/2,
self.view.frame.width, self.view.frame.height)
//content size of the scrollview
scrView2.contentSize = CGSizeMake(self.view.frame.width*3, self.view.frame.height)
//Size and place of UIImageView
imgView1.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
imgView2.frame = CGRectMake(width, 0, self.view.frame.width, self.view.frame.height)
imgView3.frame = CGRectMake(width*2, 0, self.view.frame.width, self.view.frame.height)
//adding the scrollview to the view
self.view.addSubview(scrView2)
scrView2.addSubview(imgView1)
scrView2.addSubview(imgView2)
scrView2.addSubview(imgView3)
scrView2.pagingEnabled = true
//Initial location of the scrollview
scrView2.contentOffset = CGPointMake(0, 0);
I would like to know how I can make the scrollview to show the bottom part of the image, and not the top part. Will you help me out?
I made this code, hope it will work for you. I used the storyboard, made a scrollView, added an imageView and added constrains to the image view to stick to the edges (don't know it that's necessary).
imageView = UIImage(named: "image")
scrollView.contentOffset = CGPoint(x: 0, y: image.image!.size.height)

Resources