Swift. Flip animation. FromView is not displayed - ios

I've wrote this code to create a simple flip animation:
func frontView (view:UIView) ->UIView {
var frontView: UIView
frontView = UIView()
frontView.frame = view.frame
frontView.center = CGPoint(x: 0, y: 0)
return frontView
}
func backView (view:UIView) ->UIView {
var backView: UIView
backView = UIView()
backView.frame = view.frame
backView.backgroundColor = UIColor.redColor()
backView.center = CGPoint(x: view.frame.width/2, y: 0)
view.addSubview(backView)
return backView
}
func flipViewAnimation (viewToAnimate: UIView) {
var animationOption = self.animationOption
var duration = self.duration
UIView.transitionFromView(backView(viewToAnimate), toView: frontView(viewToAnimate), duration: duration, options: animationOption, completion: nil)
//
}
As a viewToAnimate I use views with labels and imageViews inside, which I've created in AutoLayout. The result I'm trying to achieve more or less should look like this. First I see views filled with color, then they flip and show the content inside (labels and ImageViews).
But it works in a different way. Views appear already with content (labels and ImageViews) then just flip and again show the same content.

I've created each view programmatically and it works very well for me now.

Related

Animate image crop from right to left

I want to animate a change in the width of an ImageView in Swift, in a way it will be appeared as if the image is being cropped from right to left. I mean that I want the image to always stick to the left edge of its superView and only its right edge will be changed during the animation, without changing the image scale.
I've managed to animate the change in the width of the image while preserving its scale, but the image is being cropped from both sides towards its centerX and not from right to left only.
imageView.contentMode = .scaleAspectFill
imageView.clipToBounds = true
let animation = CABasicAnimation(keyPath: "bounds.size.width")
animation.fromValue = 250
animation.toValue = 50
imageView.layer.add(animation, forKey: nil)
According to this SO thread one should change the anchorPoint of imageView.layer to have x=0, but doing so moves the left edge of the image to the center of the view, and also moves the image when animating so that when it is in its smaller width, the image's centerX point will be visible at the center of the screen.
I would suggest you to use additional view (panel) that defines the size and place your image view on this view. Panel may then clip the image view to create a desired effect.
I created a short example all in code just to demonstrate the approach:
class ViewController: UIViewController {
private var isImageExtended: Bool = true
private var imagePanel: UIView?
override func viewDidLoad() {
super.viewDidLoad()
let panel = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 100.0))
panel.backgroundColor = .lightGray
let imageView = UIImageView(image: UIImage(named: "test_image"))
imageView.frame = CGRect(x: 0.0, y: 0.0, width: panel.bounds.width, height: panel.bounds.height)
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
panel.addSubview(imageView)
imageView.backgroundColor = .red
view.addSubview(panel)
panel.center = view.center
panel.clipsToBounds = true
imagePanel = panel
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
}
#objc private func onTap() {
guard let imagePanel else { return }
isImageExtended = !isImageExtended
let panelWidth: CGFloat = isImageExtended ? 200 : 50
UIView.animate(withDuration: 0.5) {
imagePanel.frame.size.width = panelWidth
}
}
}
I could easily achieve the same result using storyboard and constraints with significantly less code:
class ViewController: UIViewController {
#IBOutlet private var panelWidthConstrain: NSLayoutConstraint?
private var isImageExtended: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
}
#objc private func onTap() {
isImageExtended = !isImageExtended
let panelWidth: CGFloat = isImageExtended ? 200 : 50
UIView.animate(withDuration: 0.5) {
self.panelWidthConstrain?.constant = panelWidth
self.view.layoutIfNeeded()
}
}
}
I hope the code speaks enough for itself.

How can I fill image in scroll view to fill screen?

I am playing around with scroll views, and I've run into an issue I'be stuck with. I have a view controller create in Storyboard. The view controller contains a scroll view which fills the entire superview.
I then added the images programmatically to the scroll view. The images do show within the scroll view and paging works just fine. Only problem is the scroll view is set ti fill superview but the image view that hold the images seems like it stops above where the navigation bar would be. How can I have the image view fill the whole view within the scroll view?
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pagingView: UIPageControl!
var images = [UIImage]()
var frame = CGRect(x: 0, y: 0,width: 0,height: 0)
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.isPagingEnabled = true
images = [UIImage(named: "Slide1")!, UIImage(named: "Slide2")!, UIImage(named: "Slide3")!, UIImage(named: "Slide4")!]
pagingView.numberOfPages = images.count
// This is where I think I'm having the height problem.
for i in 0..<images.count {
let imageView = UIImageView()
let x = self.view.frame.size.width * CGFloat(i)
imageView.frame = CGRect(x: x, y: 0, width: self.view.frame.width, height: self.view.frame.height)
imageView.contentMode = .scaleAspectFill
imageView.image = images[i]
scrollView.contentSize.width = scrollView.frame.size.width * CGFloat(i + 1)
scrollView.addSubview(imageView)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pagingView.currentPage = Int(pageNumber)
}
After setting nav bar to hidden, here is the output
Scroll view background color is red
In this case you need to enable the parent view clipsToBounds. Set UIScrollview clipsToBounds property to True.
Programmatically scrollView.clipsToBounds = true
In UIStoryBoard - Click the view->Attributes Inspector
If you would like to see the whole screen, make sure to add the topConstraint of scrollView assigned superView and hide the navigationBar in viewWillAppear,
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
Make sure to remove the status bar by
override var prefersStatusBarHidden: Bool {
return true
}
Update the Y position of Image.
imageView.frame = CGRect(x: x, y: **self.scrollView.frame.minY**, width: self.view.frame.width, height: self.view.frame.height)
Update the scrollView topConstraint by -20.

How to include an ImageView into a Custom Segue?

I'm attempting to create a custom segue where the order of the animation goes
1st UIViewController -> ImageView -> 2nd UIViewController
The code I have works, but for some reason the ImageView is black. I've tried using various images and literal image sources, but it's still black.
Here's some pictures for a better understanding-
1 (As the first UIViewController is replaced by the ImageView)
2 (As the ImageView is replaced by the second UIViewController)
/**
This segue transitions by dragging the destination UIViewController in from the right and replacing the old UIViewController by pushing it off to the left. This Segue also contains an ImageView that is between the two UIViewControllers.
*/
class CustomSegue2: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
let firstView = self.source.view as UIView!
let secondView = UIImageView()
let thirdView = self.destination.view as UIView!
// Get the screen width and height.
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
// Set properties of secondView
secondView.frame = CGRect(x: width, y: 0, width: width, height: height)
secondView.contentMode = UIViewContentMode.scaleToFill
secondView.image = #imageLiteral(resourceName: "orangeRedGradient_MESH_tealGreenGradient")
// Specify the initial position of the destination view.
let rect = CGRect(x: width + width, y: 0.0, width: width, height: height)
thirdView?.frame = rect
// Access the app's key window and insert the destination view above the current
let window = UIApplication.shared.keyWindow
window?.insertSubview(thirdView!, aboveSubview: firstView!)
// Animation the transition.
UIView.animate(withDuration: 10, animations: { () -> Void in
firstView?.frame = firstView!.frame.offsetBy(dx: -width-width, dy: 0.0)
secondView.frame = secondView.frame.offsetBy(dx: -width-width, dy: 0.0)
thirdView?.frame = thirdView!.frame.offsetBy(dx: -width-width, dy: 0.0)
}) { (Finished) -> Void in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
secondView never appears because at no time do you insert it into the interface.
(I would describe your code overall as completely wrong-headed — if you want a custom transition, you should be writing proper custom transition animation code. But that answers the question of why the image view is "black" — it isn't black, it is completely absent.)

How to Add UIToolbar with two button in UIPickerView(swift 2.0)?

I would like to add two UIButtons in the UIPickerView (on bottom of it). Please take a look at the Cancel and Done buttons in this image:
Here My code Upload:-
class DatePicker{
var containerView = UIView()
var datePicker = UIView()
var datePickerView = UIDatePicker()
var toolBar = UIToolbar()
internal class var shared: DatePicker {
struct Static {
static let instance: DatePicker = DatePicker()
}
return Static.instance
}
internal func showProgressView(view: UIView) {
containerView.frame = view.frame
containerView.center = view.center
containerView.backgroundColor = UIColor(hex: 0xffffff, alpha: 0.3)
datePickerView.datePickerMode = .Date
datePicker.frame = CGRectMake(0, 0, 250, 250)
datePicker.center = view.center
datePicker.backgroundColor = UIColor(hex: 0x444444, alpha: 0.7)
datePicker.clipsToBounds = true
datePicker.layer.cornerRadius = 10
datePickerView.setValue(UIColor.whiteColor(), forKeyPath: "textColor")
datePickerView.frame = CGRectMake(0, 0, 250, 250)
datePicker.addSubview(datePickerView)
containerView.addSubview(datePicker)
view.addSubview(containerView)
}
internal func hideProgressView() {
containerView.removeFromSuperview()
}
}
How can i get UIToolbar And Two Button?
I would recommend making a .xib with the toolbar and picker view in it. Put two bar buttons on the bar and put a bar button spacer in between them.
Link on how to use a xib
You can make the animation of it appearing look great if you start its frame off screen and use this function to move it on screen.
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
if viewToMove != nil {
viewToMove!.frame.origin.y/*orX*/ = onScreenPosition
} else {
"sidePanel == nil"
}
}, completion: completion)
usingSpringWithDamping can be set to 1 if you don't want any bounce in animation. If this is for an iPhone, i would recommend making the initial frame equal to something like
CGRectMake(0, UIScreen.mainScreen().bounds.height, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height/2)
and then the view to move line would become
viewToMove!.frame.origin.y = UIScreen.mainScreen().bounds.height/2
using the previous function to bring it on screen and
viewToMove!.frame.origin.y = UIScreen.mainScreen().bounds.height
to move it off screen. You can access the buttons of the xib in a few different ways, but I recommend using a delegate.
Here is a guide to them.
If you are not familiar with them, I would become familiar, because they are very useful! If you have any questions don't hesitate to ask!

When to use UITouch vs UIScroll

I would like to achieve the design you see in dating apps. Where you can vertically scroll images of a profile and also horizontally scroll to view the next or previous person in the list.
Currently I have my views laid out as such.
Previous-UIView - current UIView - next UIView
UIScrollView. UIScrollView. UIScrollView
Images. Images. Images
UIView. UIView. UIView
Profile info. Profile info. Profile info
UIPageControl. UIPageControl UIPageControl.
Only one of the Views occupies the main view with next and previous off screen. Ideally when the user moves the view left I would programmatically remove the previous view, make current the previous, the next current and add a new view for next. Visa versa for moving right.
What is the best way to scroll the views horizontally?
Should I wrap them all in a UIScrollView? And would that interfere with the UIScrollView sub Views?
Or should I program touch controls to move the views?
Or is there a better way?
I'm still a newbie at iOS development so any help would be greatly appreciated.
So I've tried some experimenting with a test app and I'm pleased to say you can have UIScrollviews inside UIScrollviews.
I was able to get it running perfectly. Here is my code below.
override func viewDidLoad() {
super.viewDidLoad()
self.superView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
var subImages1 = ["IMG_0004.JPG","IMG_0005.JPG","IMG_0008.JPG"]
var subImages2 = ["IMG_0009.JPG","IMG_0010.JPG","IMG_0011.JPG"]
var subImages3 = ["IMG_0013.JPG","IMG_0017.JPG","IMG_0018.JPG"]
self.images.append(subImages1)
self.images.append(subImages2)
self.images.append(subImages3)
self.superView.frame = self.view.frame
self.superView.contentSize = CGSizeMake(self.view.frame.width*3, self.view.frame.height)
self.superView.contentOffset = CGPoint(x:self.view.frame.width,y:0)
self.superView.pagingEnabled = true
self.view.addSubview(self.superView)
//layout the UIVeiws into the master ScrollView
for i in 0...2{
var offset = self.view.frame.width * CGFloat(i)
var pView = UIView()
pView.frame = CGRectMake(offset, 0, self.view.frame.width, self.view.frame.height)
pView.backgroundColor = colours[i]
self.superView.addSubview(pView)
self.profileViews.append(pView)
}
// Add sub Scroll views and images to the Views.
for (index, view) in enumerate(self.profileViews){
var scrollView = UIScrollView()
scrollView.delegate = self
scrollView.frame = CGRectMake(10, 10, self.view.frame.width-20, self.view.frame.height-20)
scrollView.pagingEnabled = true
scrollView.contentSize = CGSizeMake(scrollView.frame.width, scrollView.frame.height * CGFloat(images[index].count))
for (index2, image) in enumerate(images[index]){
var subImage = UIImageView()
subImage.frame = CGRectMake(0, scrollView.frame.height * CGFloat(index2), scrollView.frame.width, scrollView.frame.height)
subImage.contentMode = UIViewContentMode.ScaleAspectFit
subImage.image = UIImage(named: image as! String)
scrollView.addSubview(subImage)
}
view.addSubview(scrollView)
self.scrollViews.append(scrollView)
}
}
//Use the did end decelerating as it executes the code once the scoll has finished moving.
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if(scrollView == self.superView){
var contentOffset = scrollView.contentOffset
var pageWidth = self.superView.frame.width
var fractionalPage:Double = Double(self.superView.contentOffset.x / pageWidth)
var page = lround(fractionalPage)
// In this example I take the last UIView from the stack and move it to the first.
// I would do the same in the real app but update the contents of the view after
if(page == 0){
var tempView = self.profileViews[2]
self.profileViews[2].removeFromSuperview()
self.profileViews.removeAtIndex(2)
for view in self.profileViews{
view.frame = CGRectMake(view.frame.minX + self.view.frame.width, 0, view.frame.width, view.frame.height)
println(view.frame)
}
tempView.frame = CGRectMake(0, 0, tempView.frame.width, tempView.frame.height)
self.profileViews.insert(tempView, atIndex: 0)
self.superView.addSubview(tempView)
var newOffset = contentOffset.x + pageWidth
self.superView.contentOffset = CGPoint(x: newOffset, y: 0)
}
// Take the first view and move it to the last.
if(page == 2){
var tempView = self.profileViews[0]
self.profileViews[0].removeFromSuperview()
self.profileViews.removeAtIndex(0)
for view in self.profileViews{
view.frame = CGRectMake(view.frame.minX - self.view.frame.width, 0, view.frame.width, view.frame.height)
println(view.frame)
}
tempView.frame = CGRectMake(tempView.frame.width*2, 0, tempView.frame.width, tempView.frame.height)
self.profileViews.append(tempView)
self.superView.addSubview(tempView)
var newOffset = contentOffset.x - pageWidth
self.superView.contentOffset = CGPoint(x: newOffset, y: 0)
}
}
}

Resources