I have a UITableViewController with a UIImageView. The content is loaded with the following simple code.
#IBOutlet weak var avatarImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func updateUI() {
backendless.userService.currentUser.retrieveProperties()
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2
avatarImageView.layer.masksToBounds = true
let imgURL = NSURL(string: (backendless.userService.currentUser.getProperty("Avatar") as? String)!)
avatarImageView.sd_setImageWithURL(imgURL)
tableView.reloadData()
}
The ImageView does not appear.
But if I delete the following code, the UIImageView will be set and displayed correctly.
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2
avatarImageView.layer.masksToBounds = true
What am I missing? Help is very appreciated.:
PS:
Xcode 8.0
Swift 2.3
Contrains for ImageView are set to 120*120
sd_setImageWithURL = pod 'SDWebImage'
Try this:
override func viewDidLoad() {
super.viewDidLoad()
self.view.layoutIfNeeded() // <- This will update your initial view frames
updateUI()
}
What is happening is that in Xcode 8.0, by the time you hit viewDidLoad, the frame of all subviews of a controller is still x: 0.0, y: 0.0, w: 1000.0, h: 1000.0, so in effect you're making the corner radius of your avatar image view 500.0pt. When you call layout if needed, the correct frames will be calculated, and give you the value you're expecting.
One obvious problem is that you are calling updateUI too soon. In viewDidLoad, there is no UI. The view has loaded but that is all; it is not in the interface and no layout has yet taken place. Your code depends upon avatarImageView.frame, but we do not yet know what that is. You have constraints on the image view, but they have not yet taken effect! Thus you should postpone this call until after the interface is present, e.g. viewDidAppear or at least viewDidLayoutSubviews. But of course then you probably only want to call it once (typed in browser, not tested, but it will be something like this):
var didConfigureUI = false
func viewDidLayoutSubviews() {
if !didConfigureUI {
didConfigureUI = true
updateUI()
}
}
I think your problem with your controller. You have added the image view to a UITableViewController. That means constraint set to image view will not work if you use the frame in view did load. You need to change frame of the UIIMageView before use it or you need to use UIViewController. use the below code before using the frame of image view.
avatarImageView.frame.size.width = 120.0
avatarImageView.frame.size.height = 120.0
I think this will solve the problem. But you should use UIViewController if there is no special purpose of UITableViewController.
āxy.view.layoutIfNeeded()ā
needs to be called everytime iām calling a property before the view has finished loading
for example imageView.view.layoutIfNeeded() to call for the height and widths values
Related
I'm presenting my UIViewController with modalPresentationStyle = .formSheet. Everything works as expected except interactive dismissing this controller (I drag form sheet from the top down). Controller is dismissed but it's presentingViewController doesn't move back. It remains stucked with black edges around.
If I dismiss presented controller using dismiss(animated:completion:), presentingViewController moves back as expected so it seems it doesn't work just when user dismiss it by dragging.
If you need any other details let me know. Thank you. š
I'm using iOS 13.3 and Xcode 11.2.1.
Update:
I found a problem. š This view which I presented contained UITableView. I was using my method to set its tableFooterView:
extension UITableView {
func setFooterView(_ view: UIView) {
view.frame = bounds
view.setNeedsLayout()
view.layoutIfNeeded()
let height = view.systemLayoutSizeFitting(
CGSize(width: view.frame.width, height: UIView.layoutFittingCompressedSize.height)
).height
var frame = view.frame
frame.size.height = height
view.frame = frame
tableFooterView = view
sectionFooterHeight = 0
}
}
...and I called this method in my view controller in viewDidLayoutSubviews() since I thought that this is where I should set this footer view (Simply because we were using it like this in various projects š¤·š»āāļø):
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.setFooterView(footerView)
}
But I wasn't right. Simply calling it in viewDidLoad() resolved it.
override func viewDidLoad() {
super.viewDidLoad()
tableView.setFooterView(footerView)
}
No the question is: why?
If anyone could provide correct explanation why this happens I would be grateful and I would reward him/her with bounty. š
So I have a big image in an UIImageView (Swift) with "Aspect Fill"
But What I want is to have like an Animation or Ken Burn Effect on the Image.
In the Image Below... What I want is to go from Red to Yellow seen the image.
Take a normal uiview give it proper height width as per ur wish... Take an imageview inside of it and give the height, x coordinate and y coordinate same as uiview and keep the width as much wide you want to keep the image....
Use UIView.animateWithDuration method and in its animation block set the final desired frame such that the end of image view comes equal to the end of uiview... The image view will slide outside of vision of the uiview as you desire...
You can even repeatedly animate from left to right calling animation into each others completion block
I found an extension for UIImageView called UIImageViewAligned. You can use the alignment properties provided by the extension and animate the changes.
import UIKit
import UIImageViewAligned
class ViewController: UIViewController {
// MARK: Outlets
#IBOutlet var imageView: UIImageViewAligned!
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
imageView?.contentMode = .scaleAspectFill
imageView?.alignLeft = true
imageView?.alignRight = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 5) {
self.imageView?.alignLeft = false
self.imageView?.alignRight = true
}
}
}
I'm writing a function which can get the height and width of the container view:
// container view's UIViewController
class SelectionView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func getBounds -> (CGFloat,CGFloat){
let x = self.view.bounds.width / 5
let y = self.view.bounds.height / 15
return x,y
}
}
I write a button to call this getBounds() and it works well, but when I put it in the viewDidLoad() function
override func viewDidLoad() {
super.viewDidLoad()
getBounds()
}
getBounds() returns me different height and width and it is clearly not the bounds of this container view.
I'm pretty sure I've linked this class to the container view!
View layout is not setup in viewDidLoad. Therefore any resizing is not done yet and your size is wrong(probably the same as declared in Storyboard/Xib).
Move getBounds in viewWillLayoutSubviews or viewWillAppear and it will work correctly. Please mind that those method won't be called one time only ;)
The view hasn't been laid out in viewDidLoad, you will likely need to catch it in a later method or in viewDidLayoutSubviews.
So this is the weirdest thing I came across ever.
I add a simple UIView on top of an UIView on an UIViewController. I change the size to 200 x 200 and then I want to change the UIView's size by placing this line of code inside the viewDidLoad method:
self.gameBackgroundView.frame.size.height = 10
which seems perfectly normal in Swift. Though, nothing happens. I can change the color, corner radius etc...
EDIT:
I have also deleted all constraints from that view and it still does not work.
Ok, So I found a solution here on stack ..
Change frame programmatically with auto layout
The correct answer, that works is, that:
I need to create an IBOutlet of my heightConstraint
Change the value of that constraint accordingly
Thank you all!
It is because of Auto Layout, however, you could do it after Auto Layout is done its work or change constraints of it. Set it your frame in your viewDidAppear() instead. It should work.
I hope this helps you.
Try this code, it will work on any device
let BackImage = UIImage(named: "backGrndImg#2x.png")!
var imageView: UIImageView!
override func loadView() {
super.loadView()
self.imageView = UIImageView(frame: CGRectZero)
self.imageView.contentMode = .ScaleAspectFill
self.imageView.image = BackImage
self.view.insertSubview(imageView, atIndex: 0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.imageView.frame = self.view.bounds
}
I have watched the WWDC 2012 presentations on Auto Layout and read the documentation on the view appearance calls.
So I thought I needed to perhaps wait a frame or a second after viewDidAppear just to be safe, but still didn't work. Here was my code:
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
view.layoutIfNeeded()
view.autoresizesSubviews = false
_textButton.frame = CGRectMake(0, 0, 0, 0)
println(_textButton.frame)
let delay:Double = 4*Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue())
{
self._textButton.frame = CGRectMake(0, 0, 0, 0)
println(self._textButton.frame)
}
}
This actually prints out (144.0,6.5,32.0,32.0) twice in a row. Which means even AFTER setting the frame to 0, it was set back to its constraint defaults.
Why is this?
I have another ViewController that looks almost the same as this one, with buttons having the same constraints. But when I close the view, I animate the buttons to slide out to the left with the following code:
#IBAction func takePhotoTap(sender: AnyObject)
{
UIView.animateWithDuration(0.5, animations: animations)
_camera.captureImage(receivePhoto)
}
func animations()
{
var height = CGFloat(_distanceBetweenTopAndMiddleBar)/2
_lowerLens.frame = CGRectMake(_lowerLens.frame.origin.x, _lowerLens.frame.origin.y, _lowerLens.frame.width,-height)
_upperLens.frame = CGRectMake(_upperLens.frame.origin.x, _upperLens.frame.origin.y, _upperLens.frame.width,height)
view.autoresizesSubviews = false
slideOffScreenLeft(_gridLinesButton)
slideOffScreenLeft(_swapCameraButton)
slideOffScreenLeft(_flashButton)
}
func slideOffScreenLeft(obj:UIView)
{
obj.frame = CGRectMake((-obj.frame.width), obj.frame.origin.y, obj.frame.width, obj.frame.height)
}
This works JUST FINE! When they hit the button, these buttons slide off the screen. However when I load the next view I want the buttons to slide in from the right. But, as you can see above, even waiting 4 seconds before trying to set the frames of the buttons has no effect.
Can you suggest what I can do to animate some buttons to slide in the from the screen when the view loads? Why are the constraints overriding my changes in the first case but when I animated a View Controller before closing it with UIView.animateWithDuration the constraints were overriden?
When using AutoLayout, it is best practice to create outlets for your constraints and then modify the constraints, rather than the frames of your objects. I was having a similar problem. This also gets rid of the need for the dispatch_after call.
For example:
#IBOutlet weak var buttonHeight: NSLayoutConstraint!
#IBOutlet weak var buttonWidth: NSLayoutConstraint!
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
// Modify the constraint rather than the frame
self.buttonHeight.constant = 0
self.buttonWidth.constant = 0
}
This will make the button 0x0. You can then do the same for the x,y position.