Animating constraint results in different initial position - ios

I have a green view, which is pinned to the edges of the top view. On top is a blue view, which is also a child of the top view and which is aligned using the following code...
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var blueView: UIView!
#IBOutlet weak var greenView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
blueView.translatesAutoresizingMaskIntoConstraints = false
blueView.widthAnchor.constraint(equalTo: greenView.widthAnchor).isActive = true
blueView.leftAnchor.constraint(equalTo: greenView.leftAnchor).isActive = true
blueView.heightAnchor.constraint(equalTo: greenView.heightAnchor).isActive = true
let top = blueView.topAnchor.constraint(equalTo: greenView.topAnchor)
top.priority = 800
top.isActive = true
view.layoutIfNeeded()
UIView.animate(withDuration: 5) {
let bottom = self.blueView.topAnchor.constraint(equalTo: self.greenView.bottomAnchor)
bottom.priority = 1000
bottom.isActive = true
self.view.layoutIfNeeded()
}
}
}
If I comment out the animation code, this is how it looks...
With the animation block, the blue view finishes in the correct place, but it starts at the top of the screen instead of aligned to the top of the green view.
Why is the animation block causing the blue view to start further up the screen?

OK - the Blue view is not starting at the top of the Green view because you are starting your animation before the initial layout has finished.
You could move the animation block into a function triggered after a delay, or into viewDidAppear:
import UIKit
class AnimViewController: UIViewController {
#IBOutlet weak var blueView: UIView!
#IBOutlet weak var greenView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
blueView.translatesAutoresizingMaskIntoConstraints = false
blueView.widthAnchor.constraint(equalTo: greenView.widthAnchor).isActive = true
blueView.leftAnchor.constraint(equalTo: greenView.leftAnchor).isActive = true
blueView.heightAnchor.constraint(equalTo: greenView.heightAnchor).isActive = true
let top = blueView.topAnchor.constraint(equalTo: greenView.topAnchor)
top.priority = 800
top.isActive = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 5) {
let bottom = self.blueView.topAnchor.constraint(equalTo: self.greenView.bottomAnchor)
bottom.priority = 1000
bottom.isActive = true
self.view.layoutIfNeeded()
}
}
}

Related

How to draw a dot on an image with zoom in/out capability?

I have an imageview in scrollview (I followed this video https://www.youtube.com/watch?v=0Tz0vI721c8). I want to draw a green dot on that image at x=100, y=100 (from upper left corner of the image). When user zoom in/out and pan, the dot stays at that location (image coord 100,100). How do I do that? I am totally new to ios dev.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupScrollView()
}
func setupScrollView() {
scrollView.delegate = self
}
}
extension ViewController:UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
}
Create a imageView(dot) to Frame layout, attach dot align top and leading to Frame layout.
Try ur relative object(dot) layout to absolute object(imageView).
let dot = UIView.init()
dot.backgroundColor = .red
//Use layout
dot.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(dot)
let left = dot.leadingAnchor.constraint(equalTo: imageView.leadingAnchor, constant: 100)
let top = dot.topAnchor.constraint(equalTo: imageView.topAnchor, constant: 100)
let width = dot.widthAnchor.constraint(equalToConstant: 10)
let height = dot.heightAnchor.constraint(equalToConstant: 10)
var layout:[NSLayoutConstraint] = [left,top,width,height]
NSLayoutConstraint.activate(layout)

UIView: how does the appearance() proxy work?

I have created a simple custom UIView:
final class TestView: UIView {
var testColor: UIColor = .white {
didSet {
backgroundColor = testColor
}
}
}
Then I wrote this in my view controller:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var testView: TestView!
#IBOutlet weak var testView2: TestView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
TestView.appearance().testColor = .red
}
}
}
By doing this, I get an error:
Could you help me understanding what's wrong here and how to implement the UIAppearance proxy for any custom UIView?
Thank you for your help
You need to make the property #objc and dynamic:
final class TestView: UIView {
#objc dynamic var testColor: UIColor? = .white {
didSet {
backgroundColor = testColor
}
}
}
Worth noting: the UIAppearance proxy does NOT affect views which are already part of the view hierarchy.
So, in your example, adding #objc dynamic to your property will get rid of the crash, but you will not see any change to the two #IBOutlet views.
If you call it as part of viewDidLoad() (instead of DispatchQueue.main.asyncAfter):
override func viewDidLoad() {
super.viewDidLoad()
TestView.appearance().testColor = .red
}
The two #IBOutlet views will get the red background.
Or, if you add a new view to the hierarchy, it will get the red background:
class AppearanceTestViewController: UIViewController {
#IBOutlet weak var testView: TestView!
#IBOutlet weak var testView2: TestView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
TestView.appearance().testColor = .red
self.addAnotherTestView()
}
}
func addAnotherTestView() -> Void {
let v = TestView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
NSLayoutConstraint.activate([
v.widthAnchor.constraint(equalToConstant: 240.0),
v.heightAnchor.constraint(equalTo: v.widthAnchor),
v.centerXAnchor.constraint(equalTo: view.centerXAnchor),
v.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
// this newly added view WILL have a red background
}
}

Access ScrollView Delegate Method in another Class

I have a UIScrollView Hooked up to 2 View Controllers. like this
let V1 = self.storyboard?.instantiateViewControllerWithIdentifier("HomeScreen") as UIViewController!
//Add initialized view to main view and its scroll view and also set bounds
self.addChildViewController(V1)
self.scrollVieww.addSubview(V1.view)
V1.didMoveToParentViewController(self)
V1.view.frame = scrollVieww.bounds
//Initialize using Unique ID for the View
let V2 = self.storyboard?.instantiateViewControllerWithIdentifier("MainScreen") as UIViewController!
//Add initialized view to main view and its scroll view also set bounds
self.addChildViewController(V2)
self.scrollVieww.addSubview(V2.view)
V2.didMoveToParentViewController(self)
V2.view.frame = scrollVieww.bounds
//Create frame for the view and define its urigin point with respect to View 1
var V2Frame: CGRect = V2.view.frame
V2Frame.origin.x = self.view.frame.width
V2.view.frame = V2Frame
//The width is set here as we are dealing with Horizontal Scroll
//The Width is x3 as there are 3 sub views in all
self.scrollVieww.contentSize = CGSizeMake((self.view.frame.width) * 2, (self.view.frame.height))
Now i can use The ScrollView Delegate Method which is written inside this class.
So now i went up to my class Main_Screen and subclass it with UIScrollViewDelegate.
Code for Main_Screen Class
import UIKit
public class Main_Screen: UIViewController , UIScrollViewDelegate {
#IBOutlet weak var aboutMebtn: UIButton!
#IBOutlet weak var studybtn: UIButton!
#IBOutlet weak var appsbtn: UIButton!
#IBOutlet weak var skillsbtn: UIButton!
var scrollView : UIScrollView!
#IBOutlet var avatarImageView: UIImageView!
weak var pageControl: UIPageControl!
var mainRead : Bool = false
override public func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Making the view round
for v : UIView in self.view.subviews {
if v.tag != 1 {
v.layer.cornerRadius = v.frame.size.width/2
v.layer.masksToBounds = true
}
}
}
public func animate(){
// I get a fatal error here
var buttons : Array<UIButton> = [aboutMebtn , studybtn , appsbtn , skillsbtn]
avatarImageView.alpha = 0.0
avatarImageView.transform = CGAffineTransformMakeScale(1.25, 1.25)
UIView.animateWithDuration(1.0, delay: 1.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .CurveEaseIn, animations: { () -> Void in
self.avatarImageView.alpha = 1.0
self.avatarImageView.transform = CGAffineTransformMakeScale(0.75, 0.75)
}, completion: nil)
}
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if(scrollView.contentOffset.x == self.view.frame.size.width * 1 ){
print("Main is read")
}
}
But the method of UIScrollView Delegate wont execute. i can do it in ViewController but i cant as i want to run the animate function Above on the scrollViewDidEndDeclerating Method
you must assign your view controller as UIScrollView delegate
self.scrollVieww.delegate = self;
you probably miss it.
you can make it inside viewDidLoad

Weird Button Animation

It's my first try on IOS animation, so if I'm worry from the very beginning, just tell me the right direction.
All I want: when I click ButtonOne, Label disappears slowly.
The code as below:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var labelHeight: NSLayoutConstraint!
private var isHidden: Bool = false
#IBAction func clickButtonOne(sender: UIButton) {
isHidden = !isHidden
if isHidden {
labelHeight.constant = 0
} else {
labelHeight.constant = 60
}
UIView.animateWithDuration(1.0, animations: {
() -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Before I click ButtonOne:
After I click ButtonOne(label shrinks from bottom to top):
The UILabel is disappearing, but it's content is still visible.
You need to set the clipsToBounds property of the UILabel to true.
label.clipsToBounds = true
You can set the same property in the interface builder by checking the Clip Subviews property in the attributes inspector.
I think you should also animate UIView.alpha property:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var labelHeight: NSLayoutConstraint!
private var isHidden: Bool = false
#IBAction func clickButtonOne(sender: UIButton) {
isHidden = !isHidden
var alpha: CGFloat = 1
if isHidden {
alpha = 0
labelHeight.constant = 0
} else {
labelHeight.constant = 60
}
UIView.animateWithDuration(1.0, animations: {
() -> Void in
self.button.alpha = alpha
self.view.layoutIfNeeded()
}, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}

UIScrollView cannot display image either in Portrait or Landscape mode

I have created a VC with orientation in Landscape in storyboard
I have added an UIIScrollView in it , say, make it: (w)1000, 500 (h) in the VC.
What I wanted to do:
1) Scrolling the image (with high resolution like 1334 x 750) inside ScrollView
2) view the image in ScrollView in landscape mode
To make ScrollView to display the image, I have to do it in `viewDidAppear`
but here the Problems:
1) The Width and height of the `ScrollView` is gone
2) label on top gone.
3) The `ScrollView` Size become small something like 200 x 150 and start from the Top corner like (0,0)
What I need to do to make `scrollview` size like before 1000 x 500?
--- Update --
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var myUIScrollView: UIScrollView!
var imgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
//-- force to landscape mode:
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
self.myUIScrollView.maximumZoomScale = 10.0
self.myUIScrollView.minimumZoomScale = 1.0
self.myUIScrollView.delegate = self
imgView = UIImageView(image: UIImage(named: "MyPhoto.png"))
}
override func viewDidAppear(animated: Bool) {
self.myUIScrollView.contentSize = imgView.bounds.size
self.myUIScrollView.addSubview(imgView)
view.addSubview(myUIScollView)
}
override func shouldAutorotate() -> Bool {
return true
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imgView
}
Try this:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
var image: UIImage!{
get{
return myImageView.image!
}set{
myImageView.image = newValue
myImageView.sizeToFit()
myScrollView.contentSize = myImageView.frame.size
}
}
var myImageView = UIImageView()
#IBOutlet weak var myScrollView: UIScrollView!{
didSet{
myScrollView.delegate = self
myScrollView.minimumZoomScale = 10.0
myScrollView.maximumZoomScale = 1.0
myScrollView.addSubview(myImageView)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
image = UIImage(named: "one")
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return myImageView
}
}

Resources