Subview is not displaying the drawing view - ios

I am trying to draw an arc similar to this Draw segments from a circle or donut. If I add it on viewDidLoad then it works but when I am waiting for an API call and then trying to add in a reload Method subviews are not added. Any idea?
Thanks in advance!!!
override func reloadData() {
self.drawCircle()
}
func drawCircle() {
let maxArcAngle = (360 - spacingInDegree*totalLevelsRequired) / totalLevelsRequired
var previousStartAngle = 0
var previousEndAngle = 360-maxArcAngle
if let rating = userRating {
for _ in 1...totalLevelsRequired {
let vi = MView(frame: CGRect(x:16.0,y:12.0,width:40.0,height:40.0))
print(previousStartAngle)
print(previousEndAngle)
previousStartAngle = previousEndAngle - spacingInDegree
previousEndAngle = previousStartAngle - maxArcAngle
vi.backgroundColor = UIColor.clear
addSubview(vi)
}
}
}

Related

Change color to a shape added as subview

I created a UIView, called CircleView, that draws a circle. This was added with a click of a button and located with a UITapGestureRecognizer in another view, canvasView, as a subview.
This is the code for circle and it works:
#IBAction func tapCircle(_ sender: UITapGestureRecognizer) {
let tapPoint2 = sender.location(in: canvasView)
let shapeCircle = CircleView(origin: tapPoint2)
canvasView.addSubview(shapeCircle)
shapeCircle.tag = 300
}
#IBAction func circleDraw(_ sender: UIButton) {
canvasView.isUserInteractionEnabled = true
tapCircle.isEnabled = true
tapRect.isEnabled = false
tapGRQ.isEnabled = false
canvasView.setNeedsDisplay()
}
It's all okay but i'm wondering if it is possible to change the color of this shape with a click of a button. Thanks a lot.
Create global variables for arrays of shapes
var circleShapes = [UIView]()
var squareShapes = [UIView]()
...
then append new UIView to certain array after you create it
let shapeCircle = CircleView(origin: tapPoint2)
canvasView.addSubview(shapeCircle)
circleShapes.append(shapeCircle)
...
now just change backgroundColor for each view inside certain array
circleShapes.forEach { $0.backgroundColor = /* UIColor */ }
...
Or if your canvasView contains just shapes subviews, every shape has its own UIView subclass and you want to change colors in the same time, you can change backgroundColor depending on type of shape
canvasView.subviews.forEach {
switch $0 {
case is CircleView:
$0.backgroundColor = /* UIColor */
case is SquareView:
$0.backgroundColor = /* UIColor */
...
default:
continue
}
}

UIView default properties and custom properties

I created UIView with default properties
var color = Label.Color.black { didSet{ setNeedsDisplay() }}
var count = Label.Count.one.rawValue { didSet{ setNeedsDisplay() }}
var shading = Label.Shading.empty { didSet{ setNeedsDisplay() }}
var shape = Label.Shape.triangle { didSet{ setNeedsDisplay() }}
var isSelected = false
And then I add some subviews using the same UIView in ViewController
func addCardsOnGridView() {
grid.frame = cardsView.bounds
grid.cellCount = game.cardsOnDeck.count
for cardView in cardsView.subviews{
cardView.removeFromSuperview()
}
for index in 0..<grid.cellCount {
if let cellFrame = grid[index] {
let card = game.cardsOnDeck[index]
let cardView = CardsView(frame: cellFrame.insetBy(dx: CardSize.inset, dy: CardSize.inset))
cardView.color = card.label.color
cardView.count = card.label.count.rawValue
cardView.shape = card.label.shape
cardView.shading = card.label.shading
//cardView.isSelected = card.isSelected
//print(cardsView.isSelected)
cardsView.addSubview(cardView)
} else {
print("grid[\(index)] doecnt exist")
}
}
}
and then I got this UIView with default comes as superview and subviews above superview:
How can I remove view with default UIView properties and redraw with custom properties?
I can't understand your question completely. What I understand is that you want to get specific view like triangle view.
One way to achieve this is by assigning tag to every subview like below when you are adding them in mainView
aView.tag = 6
and then when you want to get a subview, you can get using viewWithTag function like below
if let aCustomView = mainView.viewWithTag(6) as? CustomView {
aCustomView.myColor = .blue
}
An other way to get custom view is by looping thought subview and try to cast view into custom class like below
mainView.subviews.forEach({ (aView) in
if aView.tag == 6,
let customView = aView as? CustomView {
customView.myColor = .blue
}
})

How can return UIView views along with its subviews in swift?

I want to create a stack of rounded circle views like this.
UIView stack
I want to return this whole stack of UIViews at once. So I tried in this way.
open func setupCirclestack(parentFrame:CGRect)->UIView
{
let arrayColor=[UIColor.yellow,UIColor.blue,UIColor.red]
let baseCircle=Circle.init(frame: parentFrame)
baseCircle.backgroundColor=UIColor.purple
var parentview=baseCircle
// var existingFrame=baseCircle.frame
for i in 0...2//<CircleValues().numberOfCircles-1
{
let circle=self.getInnerCircle(currentFrame: parentFrame)
circle.backgroundColor=arrayColor[i]
parentview.addSubview(circle)
parentview=circle as! Circle
//existingFrame=circle.frame
}
return parentview
}
func getInnerCircle(currentFrame:CGRect)->UIView
{
CircleValues.sharedInstance.radius=CircleValues.sharedInstance.radius-30
print("New Radius------\(CircleValues.sharedInstance.radius)")
let circle=Circle.init(frame: currentFrame)
return circle
}
But I can get only the last (inner most view) view. How can I return the whole stack of UIViews from this method
It is because your all the frames for all the circles are same as parentFrame.
So, your all views are adding but you can only able to see last view as it is overlaps other views!
You have to decrease your frame size for every iteration of your for loop for every child view you are adding!
for i in 0...2//<CircleValues().numberOfCircles-1
{
let circle=self.getInnerCircle(currentFrame: parentFrame) // decrease size(height and width) here every time to achieve result attached in screenshot in your question
circle.backgroundColor=arrayColor[i]
parentview.addSubview(circle)
parentview=circle as! Circle
//existingFrame=circle.frame
}
You reasign a parentView variable at the end of for loop, So it will replace existing object of parentView in which you added the circle as a subview. Therefore it will return a last circle object which is stored in parentView.
for i in 0...2//<CircleValues().numberOfCircles-1
{
let circle=self.getInnerCircle(currentFrame: parentFrame)
circle.backgroundColor=arrayColor[i]
parentview.addSubview(circle)
//parentview=circle as! Circle
//existingFrame=circle.frame
}
If you want to return views inside your parent view you need to change your function to something like this:
open func setupCirclestack(parentFrame:CGRect)->UIView
{
let arrayColor=[UIColor.yellow,UIColor.blue,UIColor.red]
let baseCircle=Circle.init(frame: parentFrame)
baseCircle.backgroundColor=UIColor.purple
var parentview=baseCircle
// var existingFrame=baseCircle.frame
for i in 0...2//<CircleValues().numberOfCircles-1
{
let circle=self.getInnerCircle(currentFrame: parentFrame)
circle.backgroundColor=arrayColor[i]
parentview.addSubview(circle)
}
return parentview
}
But if you need to return array of views, try this:
open func setupCirclestack(parentFrame:CGRect)->[UIView]
{
let arrayColor = [UIColor.yellow,UIColor.blue,UIColor.red]
let baseCircle = Circle.init(frame: parentFrame)
baseCircle.backgroundColor=UIColor.purple
var circleArray = [Circle]()
for i in 0...2
{
let circle = self.getInnerCircle(currentFrame: parentFrame)
circle.backgroundColor = arrayColor[i]
circleArray.append(circle)
}
return circleArray
}

CALayer appears only for one second, then disappears

I'm adding CALayer to top and bottom of scrollable objects (UIScrollView, TableView, CollectionView) to display them when there is a content behind the visible area.
class TableViewWithCALayers: UITableView {
var topGradientLayer: CAGradientLayer?
var bottomGradientLayer: CAGradientLayer?
override func layoutSubviews() {
super.layoutSubviews()
guard self.topGradientLayer != nil && self.bottomGradientLayer != nil else {
addGradientLayerToTop() // create layer, set frame, etc.
addGradientLayerToBottom()
return
}
// addGradientLayerToTop()// if uncomment it - multiple layers are created and they are visible, but this is not the solution...
handleLayerAppearanceAfterLayoutSubviews() // playing with opacity here
}
How I create layer:
func addGradientLayerToTop() {
if let superview = superview {
self.topGradientLayer = CAGradientLayer()
let colorTop = UIColor.redColor().CGColor
let colorBottom = UIColor.clearColor().CGColor
if let topLayer = self.topGradientLayer {
topLayer.colors = [colorTop, colorBottom]
topLayer.locations = [0.0, 1.0]
topLayer.frame = CGRect(origin: self.frame.origin, size: CGSizeMake(self.frame.width, self.layerHeight))
superview.layer.insertSublayer(topLayer, above: self.layer)
if (self.contentOffset.y == 0) {
// if we are at the top - hide layer
// topLayer.opacity = 0.0 //temporarily disabled, so it is 1.0
}
}
}
}
TableViewWithCALayers works nice everywhere, except using TableView with xib files:
class XibFilesViewController : CustomUIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: TableViewWithCALayers!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: "CustomTableViewCell")
self.tableView.layer.masksToBounds = false // this line doesn't help...
}
CustomUIViewController is used in many other ViewControllers where TableViewWithCALayers works good, so it should not create a problem.
Layers at the top and bottom appear for one second, then disappear. Logs from LayoutSubviews() func say that they are visible and opacity are 1.0, but something covers them. What can it be and how to deal with that?
Any help is appreciated!)
topLayer.zPosition = 10000 //doesn't help
topLayer.masksToBounds = false //doesn't help as well
When using nib files it's good practice, and design to add the UIView that you want to draw the layer on into your prototype cell, or header/footed and then have that view confirm to your class that's actually handling the layer.

Find UILabel in UIView in Swift

I'm trying to find my UILabels in my superview of my UIViewControllers.
This is my code:
func watch(startTime:String, endTime:String) {
if superview == nil {println("NightWatcher: No viewcontroller specified");return}
listSubviewsOfView(self.superview!)
}
func listSubviewsOfView(view: UIView) {
var subviews = view.subviews
if (subviews.count == 0) { return }
view.backgroundColor = UIColor.blackColor()
for subview in subviews {
if subview.isKindOfClass(UILabel) {
// do something with label..
}
self.listSubviewsOfView(subview as UIView)
}
}
This is how it is recommended to in Objective-C, but in Swift I get nothing but UIViews and CALayer. I definitely have UILabels in the view that is supplied to this method. What am I missing?
The call in my UIViewController:
NightWatcher(view: self.view).watch("21:00", endTime: "08:30") // still working on
Here's a version that will return an Array of all the UILabel views in whatever view you pass in:
func getLabelsInView(view: UIView) -> [UILabel] {
var results = [UILabel]()
for subview in view.subviews as [UIView] {
if let labelView = subview as? UILabel {
results += [labelView]
} else {
results += getLabelsInView(view: subview)
}
}
return results
}
Then you can iterate over them to do whatever you'd like:
override func viewDidLoad() {
super.viewDidLoad()
let labels = getLabelsInView(self.view)
for label in labels {
println(label.text)
}
}
Using functional programming concepts you can achieve this much easier.
let labels = self.view.subviews.flatMap { $0 as? UILabel }
for label in labels {
//Do something with label
}
Swift 4
Adepting mKane's answer you can use this code:
let labels = self.view.subviews.compactMap { $0 as? UILabel }
for label in labels {
// do whatever
}
You could set a tag to your UILabel in the Storyboard or programmatically using:
myLabel.tag = 1234
Then, to find it use:
let myLabel = view.viewWithTag(1234)

Resources