object value doesn't change after didset - ios

I am using some cocoa pods frame to make UICollectionView. I am able to get the right data for each cell and setup views.
I want to have different ani
The animation end point should be related to the value of dataSource.The problem is that I can't use the didSet value as I wish. The frame of subviews I added are (0,0,0,0), the values of objects keep the same as initial value outside the didSet. I am not able to access the dataSource Value in setupView function.
Expected effect is that the red bar will move from left side to the right position:
class CereCell : DatasourceCell {
override var datasourceItem: Any?{
didSet {
guard let cere = datasourceItem as? CeremonyDay else { return }
nameLabel.text = cere.name
nameLabel.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
currentBar.frame = CGRect(x: 100, y: 100, width: cere.percentage*100 , height: 10)
position.x = cere.percentage
}
}
let currentBar: UIView = {
let currentBar = UIView(frame: CGRect(x: 290, y: 100, width: 300, height: 10))
currentBar.backgroundColor = .red
return currentBar
}()
override func setupViews() {
super.setupViews()
contentView.addSubview(currentBar)
let frame = currentBar.frame
print(frame)
It just print the (290, 100, 300, 10), which is not the value in didSet.

Related

KDCircularProgressView not showing

I want to make KDCircularProgressView programmatically. I included KDCircularProgressView.swift file from https://github.com/kaandedeoglu/KDCircularProgress.
Here is code form UIViewController:
private var progressView: KDCircularProgress!
private func setupProgressView(){
progressView = KDCircularProgress(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
progressView.startAngle = 90
progressView.angle = 90
progressView.trackColor = UIColor.red
progressView.progressColors = [UIColor.white]
progressView.center = view.center
}
I am calling this function in viewDidLoad, but nothing is showing on screen.
add it to main view
self.view.addSubview(progressView)

Programmatically display UIScrollView in Swift

I have view controller with views: top level View Contains Container View which contains two views: bottomView, topView, as shown.
Scene
I want to display from: to: date range in the topView. In the bottomView I want to display a UIScrollView that contains two columns which I can scroll. I did that but the topView and BottomView overlap when I introduce scrollView. When I scroll I can see the views getting separated and as soon as i Let go the scrollbar they overlap again.
can someone tell me how to fix it? I just don't seem to understand how the scrollView and bottomView are to be associated.
Code below:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
//scrollView.frame = innerView.bounds
innerView.frame = CGRect(x:0, y:0, width:scrollView.contentSize.width, height:scrollView.contentSize.height)
}
func buildBottomView () {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let ht:Int = 21
let incrX:Int = 5
let incrY:Int = 5
let gapCol1:Int = 5
let col1Width:Int = 65
let col2Width:Int = 65
let startY:Int = 5
let col1StartX:Int = 10
let col2StartX:Int = col1StartX + col1Width + gapCol1
var loadRowStartY: Int = 0
// column headers
categoryColumnLabel.text = "Interval"
categoryColumnLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.subheadline)
//categoryColumnLabel.font = UIFont.systemFont(ofSize: 14)
categoryColumnLabel.frame = CGRect(x: col1StartX, y:startY, width: col1Width, height: ht)
categoryColumnLabel.textAlignment = NSTextAlignment.left
categoryColumnLabel.tag = 1
innerView.addSubview(categoryColumnLabel)
valueColumnLabel.text = "Values"
valueColumnLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.subheadline)
//valueColumnLabel.font = UIFont.systemFont(ofSize: 14)
valueColumnLabel.frame = CGRect(x: col2StartX, y:startY, width: col2Width, height: ht)
valueColumnLabel.textAlignment = NSTextAlignment.center
valueColumnLabel.tag = 3
innerView.addSubview(valueColumnLabel)
let sepLine:UIView = UIView()
sepLine.frame = CGRect(x: col1StartX, y:startY+ht+incrY, width: Int(screenWidth-20), height: 2)
sepLine.backgroundColor = UIColor.darkGray
sepLine.tag = 60
loadRowStartY = startY+ht+incrX+ht
innerView.addSubview(sepLine)
for i in 0 ..< 24 {
let timeIntervalLabel = UILabel()
let value2Label = UILabel()
print("display load profile")
let loadStruct = loadDict[String(i)] as! CommercialProfile
print (loadStruct.timeInterval)
timeIntervalLabel.text = loadStruct.timeInterval
timeIntervalLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.caption1)
//valueColumnLabel.font = UIFont.systemFont(ofSize: 14)
timeIntervalLabel.frame = CGRect(x: col1StartX, y:loadRowStartY, width: col1Width, height: Int(timeIntervalLabel.font.lineHeight))
timeIntervalLabel.textAlignment = NSTextAlignment.center
innerView.addSubview(timeIntervalLabel)
print(loadStruct.value)
value2Label.text = loadStruct.value
value2Label.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.caption1)
//value2Label = UIFont.systemFont(ofSize: 14)
value2Label.frame = CGRect(x: col2StartX, y:loadRowStartY, width: col2Width, height: Int(value2Label.font.lineHeight))
value2Label.textAlignment = NSTextAlignment.center
innerView.addSubview(value2Label)
loadRowStartY = loadRowStartY + incrY + Int(value2Label.font.lineHeight)
}
you are setting the bounds of the scrollView to the size of the whole view with this code: scrollView.frame = view.bounds.
The scrollView only needs to scroll the content in the bottom view. Scroll Views have their own content, that is normally larger than the viewable area of the screen/view. The scroll view just allows you to pan the viewport of that view.
So add the bottom view and setup your constraints on that. add the scrollView to the bottom view and then add your content into the scrollView.
Make sure that your bottom view has clipToBounds set to true and then you should be able to keep the headers in place and just scroll the content.
I'll try and put an example together for you shortly.
EDIT:
I've just created this simple example which shows the scroll behaviour you need. This works in a playground or just as a simple view controller. I've intentionally not used auto layout or setup constraints due to time, but you will see what you need to solve your issue
class ViewController: UIViewController {
var topView: UIView!
var bottomView: UIView!
var scrollView: UIScrollView!
var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let screenSize = UIScreen.main.bounds
self.topView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: 100))
self.bottomView = UIView(frame: CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height - 100))
self.scrollView = UIScrollView(frame: CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height - 100))
self.contentView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height * 3))
self.view.backgroundColor = .white
self.view.addSubview(self.topView)
self.view.addSubview(self.bottomView)
self.bottomView.addSubview(self.scrollView)
self.scrollView.addSubview(self.contentView)
self.bottomView.clipsToBounds = true
self.scrollView.contentSize = CGSize(width: screenSize.width, height: screenSize.height * 3)
self.contentView.backgroundColor = .gray
}
}

How to unit test UIView's frame after applying CGAffineTransform(translationX)

I want to test the following method using Quick:
func initial(states: UIView...) {
for view in states {
view.transform = CGAffineTransform(translationX: 0, y: 20)
}
}
This is the code I am using for testing (simplified version):
class ProtocolsSpec: QuickSpec {
override func spec() {
var view1: UIView!
var view2: UIView!
describe("Animator") {
beforeEach {
view1 = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
view2 = UIView(frame: CGRect(x: 80, y: 80, width: 50, height: 50))
}
it("views origin should be (0, 20) at startup") {
initial(states: view1, view2)
expect(view1.frame.origin.x) == 0
expect(view1.frame.origin.y) == 20
}
}
}
}
The tests doesn't work, the x and y are not changed.
My question is how to get the correct (x,y) coordinates after applying the transformation ?
The frame, center, and bounds properties will not change after applying transformations. The documentation of frame even states that:
If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
API Reference
In order to get the effective frame I'd suggest applying the views transform onto the views frame (using the applying(_:) method on CGRect. In your case that would look like:
let resultingFrame = view1.frame.applying(view1.transform)
expect(resultingFrame.origin.x) == 0
expect(resultingFrame.origin.y) == 20

How to load view swift more than one time?

I have one view (DataInputContainerView) and i load that view to DataController. This is my code for load DataInputContainerView to DataController
var heightInput: CGFloat = 50
lazy var inputContainerView: DataInputContainerView = {
let dataInputContainerView = DataInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.heightInput))
dataInputContainerView.dataController = self
return dataInputContainerView
}()
That code was work. But i want to load again, for example
#IBAction func loadAgain(sender: AnyObject) {
self.heightInput += 15
// Put here to load that view again
}
I want make height view +15 and load again
So you want code that will create multiple instances of your DataInputContainerView class?
Don't use a lazy property. That creates a single instance the first time it's called, and the returns the same instance each time thereafter.
Instead, create a function that returns a DataInputContainerView:
func makeDataInputContainerView(height: height: CGFloat) ->
DataInputContainerView {
let rect = CGRect(x: 0, y: 0, width: self.view.frame.width,
height: height)
let dataInputContainerView = DataInputContainerView(frame: rect)
dataInputContainerView.dataController = self
return dataInputContainerView
}
And then call it:
var height = 50
for (_ in 1...5) {
var newDataInputContainerView = makeDataInputContainerView(height: height)
self.view.addSubview(newDataInputContainerView)
}

iOS multiple CALayers being added but only one gets applied

I want to add a left/right border to my textfield. It is an IBOutlet. Also if I also add the function to style the textfield inside of the didEdit delegate callback it will update correctly.
Here is my code. Inside of ViewDidLoad.
let leftLayer = CALayer()
leftLayer.frame = CGRect(x: 0, y: 0, width: 1, height: (password.frame.size.height - 1))
leftLayer.backgroundColor = UIColor.gray.cgColor
let rightLayer = CALayer()
rightLayer.frame = CGRect(x: password.frame.size.width - 1, y: 0, width: 1, height: (password.frame.size.height - 1))
rightLayer.backgroundColor = UIColor.gray.cgColor
password.layer.setNeedsDisplay()
password.layer.addSublayer(leftLayer)
password.layer.display()
password.layer.setNeedsDisplay()
password.layer.addSublayer(rightLayer)
password.layer.display()
I've taken out all code except
let rightLayer = CALayer()
rightLayer.frame = CGRect(x: password.frame.size.width - 1, y: 0, width: 1, height: (password.frame.size.height - 1))
rightLayer.backgroundColor = UIColor.gray.cgColor
password.layer.setNeedsDisplay()
password.layer.addSublayer(rightLayer)
password.layer.display()
But iOS simply will not add this layer. I tried the snippet below
var once = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if once {
styleTextViews()
once = false
}
}
The only thing that does work is if I call it from
func textFieldDidBeginEditing(_ textField: UITextField) {
styleTextViews()
}
Then it adds it just fine..
Your problem is in ViewDidLoad you need to set this code inside viewDidLayoutSubviews because it contains the correct frame
var once = true // as the function runs multiple times
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if once {
// here
once = false
}
}

Resources