getting highly confused by odd behaviour of viewController Method - ios

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
}

Related

animated expanding of subview by action without constraints

I have a UIViewController with a UIScrollView as subview. the scrollview size and origin are computed properties.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let sizeWidth = view.frame.size.width - (view.frame.size.width / 5)
let sizeHeight = (view.frame.height / 5) * 3
let originX = (view.width-sizeWidth) / 2
// sizes & positions of container & subviews
slideScrollView.frame = CGRect(x: originX,
y: 50,
width: sizeWidth,
height: sizeHeight)
how it looks like
by tapping the sign in or login button, the scrollview should expand animated. but it is the opposite.
#objc private func loginButtonTapped() {
UIView.animate(withDuration: 5, delay: 0, options: .allowAnimatedContent) {
self.slideScrollView.frame.size.height += (self.view.frame.height / 5) * 1
}
}
this is the result
it should expand, but it sets back to the action height property and expand to regular size,
i hope anyone can tell me why this happens and may have a solution.
Thats because your viewDidLayoutSubviews gets called multiple times (In this case twice as I have noticed by adding debug statements) when you start your animation with UIView.animate(withDuration: and because you always reset the slideScrollView.frame in viewDidLayoutSubviews you see unnecessary side effects.
You can always check this by putting a break point in viewDidLayoutSubviews when loginButtonTapped gets triggered. Refer this question for similar explaination
If your intention to use viewDidLayoutSubviews is to know when view is properly loaded and its frames are updated properly you can always use
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let sizeWidth = view.frame.size.width - (view.frame.size.width / 5)
let sizeHeight = (view.frame.height / 5) * 3
let originX = (view.width-sizeWidth) / 2
// sizes & positions of container & subviews
slideScrollView.frame = CGRect(x: originX,
y: 50,
width: sizeWidth,
height: sizeHeight)
}
Now your loginButtonTapped should work fine.
As per apple docs: viewDidLayoutSubviews Called to notify the view controller that its view has just laid out its subviews.
With that I think my explanation above makes sense, you use UIView.animate(withDuration: to modify the frame of scrollView so obviously View updates/re-renders the subViews hence viewDidLayoutSubviews gets called when you call UIView.animate(withDuration: and because of your frame update code in viewDidLayoutSubviews you see adverse side effects

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.

Bottom border is not added to view in StackView (ios)

I am trying to add UIView with the bottom border programatically to stack view.But border is not getting added to UIView().
Code to add UIView to stack view:
func addOtpViews()
{
var otpLength=Int(otpLen as String)!
print("otpLength is\(otpLength)")
var i=0;
while(i<=otpLength)
{
var view=UIView(frame: .zero)
addBottomBorderTo(view: view)
otpStackView.addArrangedSubview(view)
i=i+1
}
otpStackView.translatesAutoresizingMaskIntoConstraints=false
}
on the above otpStackView is a stack view which I have already added in StoryBoard.
code to add bottom line:
func addBottomBorderTo(view:UIView) {
print("bottom is added")
let layer = CALayer()
layer.backgroundColor = UIColor.gray.cgColor
layer.frame = CGRect(x: 0.0, y: view.frame.size.height - 2.0, width: view.frame.size.width, height: 2.0)
view.layer.addSublayer(layer)
}
Why bottom line is not added to UIView()?Please help me.Thanks in advance.
have you checked the view frame here
layer.frame = CGRect(x: 0.0, y: view.frame.size.height - 2.0, width: view.frame.size.width, height: 2.0)
view.frame could be (0,0),then you will not see the bottom line.
always be careful when you work with view frame.
My suggestions:
1.Adding sub view instead of adding sub layer,then use snapkit to layout.
2.Build a seperate view class called bottomedView,then add sub view there with snapkit.
3.Build a seperate view class called bottomedView,and draw your bottom line in onDraw.
Always be aware view frame may not be determined when view is missing unexpectedly.

Change the height of UISegmented Control using IB?

How can i change the height of UISegmented control. I'm using Swift 3.0 with xcode 8. Height property is disabled by default.
I found this:
https://stackoverflow.com/a/41889155/7652057
#IBDesignable class MySegmentedControl: UISegmentedControl {
#IBInspectable var height: CGFloat = 29 {
didSet {
let centerSave = center
frame = CGRect(x: frame.minX, y: frame.minY, width: frame.width, height: height)
center = centerSave
}
}
}
https://stackoverflow.com/a/37716960/7652057
One of three options from the link,
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let rect = CGRect(origin: segment.frame.origin, size: CGSize(width: segment.frame.size.width, height: 100))
segment.frame = rect
}
Ok, I was trying to do it for a long while and here is the solution.
Firstly, It is possible within IB but for that we need to have a good bunch of autolayout constraints.
I've placed that Segmented control in a UIVIew with all the edges pinned inside it.
Then I gave the desired height to that view and it worked.
Also.. thanks to all of your answer
It's very easy. You can access it programmaticly by using frame's height:
yourSegmentedControllOutlet.frame.size.height = yourHeight

How to embed stack view in scroll view programmatically

I have tried embedding it, but my stack view is dynamic and my app is also changing orientations time to time. I have segment control at the end of the view.
I have also tried googling it but had no luck. thanks in advance.
So far I have done:
In view did load:
mainStackView.axis = UILayoutConstraintAxis.Vertical
mainStackView.spacing = 3
scrollView.frame = self.view.bounds
scrollView.addSubview(mainStackView)
view.addSubview(scrollView)
In view did layout:
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
let top = topLayoutGuide.length
let bottom = bottomLayoutGuide.length
self.mainStackView.frame = CGRect(x: 0, y: top, width: view.frame.width, height: view.frame.height - top - bottom).insetBy(dx: 10, dy: 10)
dispatch_async(dispatch_get_main_queue())
{
self.scrollView.frame = self.view.bounds
self.scrollView.contentSize = CGSize(width: self.view.bounds.width, height: self.segmentedControl.frame.origin.y + self.segmentedControl.frame.height + 50)
}
print(scrollView.contentSize)
}
You need to set the height constraint of segment control.
For Example:
segmentedControl.heightAnchor.constraintEqualToConstant(50).active = true
More over, you can Add Empty bottom view to avoid stack view's must fill mechanism. This will show you desired view output.
var bottomView = UIView(frame: CGRectZero)
stackView.addArrangedSubview(bottomView)

Resources