Detect device rotation inside a collectionViewCell - ios

Inside a custom collectionVirewCell I create a gradient layer and add it to the cell in the function override func awakeFromNib() {}
self.layer.insertSublayer(gradient, at: 0)
When the device rotates the size of the cell changes but not the size of the layer. I need to do something like
override func viewDidLayoutSubviews() {
gradient.frame = self.bounds
}
The problem is that I get an error telling me that viewDidLayoutSubviews() did not override any function from the superclass. Is there any method to detect the cell size variation inside the cell class?

viewDidLayoutSubviews() is a UIViewController method and not a method of UIView which UICollectionViewCell inherits from.
The method you're looking to override is layoutSubviews()
override func layoutSubviews() {
<#code#>
}
I think that's your problem!

Related

why does layoutSubviews get called twice during device rotation?

when i rotate device these functions got called in the exact order :
viewWillLayoutSubviews()
layoutSubviews()
viewDidLayoutSubviews()
viewWillLayoutSubviews()
layoutSubviews()
viewDidLayoutSubviews()
i was just simply rotating the device, i had simple initial default blank view controller that xcode created for me automatically, so what is the reason for app calling set of 3 layout functions for the second time?
code:
class ViewController: UIViewController {
override func viewWillLayoutSubviews() {
print(#function)
}
override func viewDidLayoutSubviews() {
print(#function)
}
}

setNeedsDisplay() is not calling draw(_ rect: CGRect)

I found similar questions in this website but none of them solved my issue. Please carefully read the whole code. The setNeedsDisplay() function is not calling the draw(_ rect: CGRect) when I want to draw a line on MyView.
I created a view called "myView" in storyboard as sub View of "MyViewController" and created a IBOutlet to the view controller.
Then I created a class called "MyViewClass" and set it as the class of "myView" in storyboard.
I set the bool value drawLine to true and call function updateLine(),
the problem is the setNeedsDispaly() is not triggering the draw(_ rect: CGRect) function.
import UIKit
class MyViewClass : UIView{
var drawLine = Bool() // decides whether to draw the line
func updateLine(){
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
if drawLine == true{
guard let context = UIGraphicsGetCurrentContext() else{
return
}
context.setLineWidth(4.0)
context.setStrokeColor(UIColor.red.cgColor)
context.move(to: CGPoint(x: 415 , y: 650))
context.addLine(to: CGPoint(x:415 , y: 550))
context.strokePath()
}
}
}
class myViewController:UIViewController {
#IBOutlet weak var insideView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let myViewInstance = MyViewClass()
myViewInstance.drawLine = true
myViewInstance.updateLine()
}
}
I'm fairly new to Swift. Any help will appreciated. Thanks.
You have 2 issues:
you're not giving the new view a frame so it defaults to zero width and zero height
you're not adding the view to the view heirarchy
let myViewInstance = MyViewClass(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height))
myViewInstance.backgroundColor = .white // you might want this a well
myViewInstance.drawLine = true
self.view.addSubview(myViewInstance)
myViewInstance.updateLine()
A cleaner way to have your view redraw when a property (such as drawLine) is changed is to use a property observer:
var drawLine: Bool = true {
didSet {
setNeedsDisplay()
}
}
That way, the view will automatically redraw when you change a property and then you don't need updateLine().
Note that setNeedsDisplay just schedules the view for redrawing. If you set this on every property, the view will only redraw once even if multiple properties are changed.
Note: viewDidLoad() would be a more appropriate place to add your line because it is only called once when the view is created. viewWillAppear() is called anytime the view appears so it can be called multiple times (for instance in a UITabView, viewWillAppear() is called every time the user switches to that tab).
MyView class
class myView : UIView {
var updateView : Bool = false {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
print("in Draw")
}
}
MyViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let view = myView()
view.updateView = true
}
}
In the storyboard i had setup the view class as myView therefore its visible.
Hopefully this will help you.

Update constraints for IBDesignable

I have a #IBDesignable UIView (ContainerView) that has one subView (also #IBDesignable). I would very much like to be able to update the constraints of the subView in the code in a way were they are automatically updated in InterfaceBuilder. Example:
#IBDesignable class ContainerView: UIView {
#IBOutlet var mySubView: MyView!
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.setup()
}
private func setup() {
self.mySubView.leadingConstraint.constant = MyResource.sizes.defaultLeading
}
}
This will work just fine at runtime, but it crashes the IBDesignablesAgent because mySubView is nil when running prepareForInterfaceBuilder.
I want to do it this way to be able to set my constraints globally in some constants, but keep the view representation in my xib files.
Does anyone have a work around for this, or am I reaching for the impossible here?

Why viewDidLayoutSubviews is called twice only on first run?

It's driving me crazy this. Only on first run the viewDidLayoutSubviews is called twice.
Here is the code I'm using:
class CICViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func addQLabel(qLabel: UILabel, questionString: String, bgrLabel: UILabel) {// some code goes here
}
func makeRoundQButtons(qButtons:[UIButton]) {
// some code goes here
}
func addArrows(numberOfArrows:Int, buttonCurAngle:Double) {//some code goes here
}
func animateButtons(qButtons:[UIButton], buttonCurAngle:Double) {
// some code goes here
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
func backTapped(sender: UIBarButtonItem) {
navigationController?.popViewControllerAnimated(false)
//some code goes here
}
func restartTapped(sender: UIBarButtonItem) {
navigationController?.popToRootViewControllerAnimated(false)
//some code goes here
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
And in my ViewController I call this :
class OneViewController: CICViewController {
override func viewDidLoad() {
super.viewDidLoad()
//some code goes here
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("viewDidLayoutSubviews")
self.makeRoundQButtons(qButtons)
self.animateButtons(qButtons, buttonCurAngle: 2.0)
}
override func viewDidAppear(animated: Bool) {
//nothing goes here
}
}
There is no guarantee as for how many times viewDidLayoutSubviews will be called.
You can find a great discussion in this Stack Overflow post:
When is layoutSubviews called?
I found this article useful. A summary from what it says:
init does not cause layoutSubviews to be called (duh)
addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target view
setFrame intelligently calls layoutSubviews on the view having it’s frame set only if the size parameter of the frame is different
scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and it’s superview
rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
removeFromSuperview – layoutSubviews is called on superview only (not show in table)

Swift IOS transparent view over original view. Overlay. Subview

I can use the following code to override drawRect() in swift, which works fine.
override func drawRect(rect: CGRect) {
var path = UIBezierPath(ovalInRect: rect)
UIColor.greenColor().setFill()
path.fill()
}
I want to add subview(or second view) over the original view.
var overlayView = UIView()
self.view.addSubview(overlayView)
My question is, how can I override the drawRect() of a subview?
Or, how can I just make the subview a new view instead and place it over the original view?
Create a custom UIView subclass, e.g.
class MyCustomView: UIView {
override func drawRect(rect: CGRect) {
// do whatever you need to do
}
}
Then replace var overlayView = UIView() with var overlayView = MyCustomView()

Resources