viewDidAppear & viewDidLoad Are Are Initializing Positions Too Late - ios

I am looking to update a UIView thats in a storyboard (and instantiated from the storyboard) when it loads in the app.
I need to position a few icons in a dynamic way (that the interface builder doesn't let me do quite yet). However, if I put my code in viewDidAppear or viewDidLoad, it seems to be getting called too late.
Here is what happens the first few second or two when the view loads:
And then a bit later it goes to the right position (as the code was called).
My question is where do I need to initialize the positions of these objects so they dont snap over a second later. viewDidLoad and viewDidAppear are too late? Why!? :)
override func viewDidAppear(animated: Bool)
{
initView()
_gridLines = false
}
func initView()
{
_cameraFeed.initAfterLoad()
//center the buttons on half distance between middle button and screen edge
var middleDistance:CGFloat = _swapButton.frame.origin.x + _swapButton.frame.width/2
_linesButton.frame.origin.x = middleDistance/2 - _linesButton.frame.width/2
_flashButton.frame.origin.x = middleDistance + middleDistance/2 - _flashButton.frame.width/2
_selectPhotos.frame.origin.x = middleDistance/2 - _selectPhotos.frame.width/2
}
Swift and objc answers welcome!

Try putting the code setting the frame in viewWillAppear instead of viewDidAppear.
Also, you should call the super.
override func viewWillAppear(animated: Bool) {
initView()
_gridLines = false
super.viewWillAppear(animated)
}

Related

When using scrollView.zoom I get different results from pressing a button to calling a function

I am trying to zoom to a button on a UIScrollView. The first function is one that I run if I segue to the vc, the second is run from a button in the view.
When running the second function it goes perfectly and centres correctly
On the first however, despite being identical code, it has an offset in the x of about 40-50 pixels (Shows a point left of the button, not any part of it) although this offset changes dependent on the button.
func moveScrollviewToRoom(){
scrollView.zoom(to: artButton.frame, animated: true)
}
#IBAction func moveScrollview(_ sender: Any) {
scrollView.zoom(to: artButton.frame, animated: true)
}
The function was called here:
override func viewDidLoad() {
super.viewDidLoad()
if moveToRoom != "" {
moveScrollviewToRoom()
}
Any help would be greatly appreciated
Cheers!
Two points...
1 - Views and subviews have not yet been positioned by auto-layout in viewDidLoad(), so the artButton.frame will not be correct.
2 - Calling any function that animates a view from viewDidLoad() won't give the desired results, because the view is not even visible yet.
Move your call to viewDidAppear():
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if moveToRoom != "" {
moveScrollviewToRoom()
}
}

UICollectionView. How Present viewController with a different element (not only first) without animation?

I need present UICollectionView, with a different element (not only first) without animation.
example
my code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
var newPoint = self.productCollectionView.contentOffset
newPoint.x = (UIScreen.main.bounds.width * CGFloat(self.currentPage)) + 1
self.productCollectionView.contentOffset = newPoint
}
}
Instead of setting contentOffset, call the setContentOffset(_:, animated:) method with an animated: value of false.
Also I would suggest removing the async and moving your code to viewDidLayoutSubviews. That way, your code will run before the user sees anything, but after the initial layout of the view has been achieved. This method can be called many times, so you will have to use a Bool instance property to make sure it only runs once.

How to change colour of scrollbar when calling flashScrollIndicator() in Swift 3

I just discovered how to properly use flashScrollIndicator but I'm having trouble changing the colour of the scroller when it flashes. I want to change it to red. This is what I have for my function to flash the scroller when the viewTable loads up:
override func viewDidAppear(_ animated: Bool) {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tableView.flashScrollIndicators()
}
I also was able to create a function that actually changes the scroller but only when the user actually scrolls, not when the scroller simply flashes. If its any help, this is the code for it:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let verticalIndicator = scrollView.subviews.last as? UIImageView
verticalIndicator?.backgroundColor = UIColor.red
}
Any help would be great! Thanks.
You just need to put those two pieces together. If you add the color-changing code to viewDidAppear, it should work just like it does in scrollViewDidScroll.
override func viewDidAppear(_ animated: Bool) {
let verticalIndicator = tableView.subviews.last as? UIImageView
verticalIndicator?.backgroundColor = UIColor.red
self.tableView.flashScrollIndicators()
}
While this is possible, I don't recommend it for a few reasons:
It's fragile. Since this isn't a readily available public API, there's no guarantee this will work in future iOS versions.
It's probably a bad design choice. Do you really want to point the user's attention to a differently colored scroll bar? Most users familiar with other apps are used to the default look of the scroll bars, so it communicates their position in the view without much extra attention. Also it doesn't look very nice when I just tried it.
The alternative solution is to simply set the scroll indicator style to one of the available options (default, white, or black).
tableView.indicatorStyle = .default

Swift: dispatch_async is effect is inconsistent for animation

I have a globalstate in my app. Depending on the state the GUI is different.
When I go from the start View A to View B I have globalstate 3
It should show an information screen, but it doesn't. BUT: When the View B has loaded only once and I jump from View C/D/E back to View B, then the code work perfectly. (You have to be in View A to get in View B.)
I use a lot dispatch_async(dispatch_get_main_queue.. that isn't good style, is it?
Why is my animation not loading at the beginning? What is good style? Thank you for answers and sorry for mistakes (english isn't my mothertongue)
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_main_queue(), {
self.animateTheInformationViewWhenGlobalStateIsThree()
})
}
func animateTheInformationViewWhenGlobalStateIsThree() {
print("GLOGBALSTATE \(globalState)") //it is 3
if globalState == 3 {
setGlobalState(3)
dispatch_async(dispatch_get_main_queue(), {
GUITools.animateTheInformationView(self.tableView, animateBottomLayout: self.animationBottomConstraint, value: self.negativValue)
})
print("THE POSITIV VALUE THE NEGATIV")
}
//GUITools-Static-Class:
class func animateTheInformationView(tableView: UITableView, animateBottomLayout: NSLayoutConstraint, value: CGFloat) {
dispatch_async(dispatch_get_main_queue(), {
animateBottomLayout.constant += value
UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in
tableView.layoutIfNeeded()
},completion: {
(value: Bool) in
})
})
}
EDIT
With viewDidAppear it works. But the animation isn't a real animation. The tableView "jumps". So there is no sliding/animation.
I deleted all dispatch_async..
override func viewDidAppear(animated: Bool) {
self.animateTheInformationViewWhenGlobalStateIsSeven()
}
viewDidLoad() does not mean that your view is already visible. Since it's not visible yet you cannot apply animations to it.
viewDidLoad() is only meant to configure your view controller's view and set up your view hierarchy - i.e. to add subviews.
What you want to use is viewWillAppear() (or viewDidAppear()) to start your animation as soon as the view becomes (or became) visible.
Also all the dispatch_async calls are most likely unnecessary. You usually only need them when you are not on the main (= UI) thread. Simply remove them.

Adding View Programmatically from ViewController super class doesn't show in front

I have a BaseViewController that my UIViewControllers extend so i can have explicit functions that i dont need to rewrite. Something i would like would be a functions such as self.showSpinner() and the viewController would show the spinner
My Code looks like this
class BaseViewController: UIViewController {
var actvIndicator : UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
self.actvIndicator = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
self.actvIndicator.color = UIColor.blackColor()
self.actvIndicator.backgroundColor = UIColor.blackColor()
self.actvIndicator.frame = CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2, 100, 100);
self.actvIndicator.center = self.view.center
self.actvIndicator .startAnimating()
self.view.addSubview(self.actvIndicator)
self.actvIndicator.bringSubviewToFront(self.view)
self.edgesForExtendedLayout = UIRectEdge.None
self.navigationController?.navigationBar.translucent = false
}
func showSpinner(){
self.actvIndicator.startAnimating()
}
func hideSpinner(){
self.actvIndicator.stopAnimating()
}
}
And my viewcontrollers looks like this
class MyProjectViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.showSpinner()
}
}
MyProjectViewController have UITableView that fills the entire screen. When i set tblProjects.alpha = 0 i can see the spinner. But i want it in the front.
i also tried self.view.bringSubviewToFront(self.actvIndicator)
What am i missing?
A couple quick notes before I get into what I think your problem is:
When you add a subview it is automatically added to the top layer, no need for the bringSubviewToFront: in viewDidLoad: (which is being used wrong anyway).
You should not set view frames in viewDidLoad: (e.g. centering a view). Frames are not setup yet, so you should move that to viewWillAppear: or some other variant.
Now your issue is most likely a view hierarchy problem (further confirmed by your comment) and thus can probably be fixed by pushing the spinner to the front every time you want it to be shown, like:
func showSpinner() {
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}
The problem here stands on the fact that table view is draw after you are calling self.view.bringSubviewToFront(self.actvIndicator). A possible workaround for this is to call bringSubviewToFront when showing the spinner
func showSpinner(){
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}

Resources