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
}
})
Related
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
}
}
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)
}
}
}
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
}
I have been search a while for this issue , I want my search bar display like BBC News App
I try all related method
for view in searchBar.subviews {
if view.isKindOfClass(NSClassFromString("UISearchBarBackground")!) {
view.removeFromSuperview()
break;
}
}
self.searchBar.tintColor = UIColor.clearColor()
self.searchBar.backgroundColor = UIColor.clearColor()
self.searchBar.translucent = true
here is my output
Am I miss something ??? Please Help me , thx !
Swift 3
To remove the background altogether, set backgroundImage to an empty image:
searchBar.backgroundImage = UIImage()
To set a custom background color, use barTintcolor property:
searchBar.barTintColor = .green
Thx all , I solve the question by setting the background image to 'nil' , which is a nonexistent image in my app
my final output
==================== Update Final Solution ====================
After read more documents . Finally I found a better solution ,
for subView in searchBar.subviews {
for view in subView.subviews {
if view.isKindOfClass(NSClassFromString("UINavigationButton")!) {
let cancelButton = view as! UIButton
cancelButton.setTitle("取消", forState: UIControlState.Normal)
cancelButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
}
if view.isKindOfClass(NSClassFromString("UISearchBarBackground")!) {
let imageView = view as! UIImageView
imageView.removeFromSuperview()
}
}
}
==================== Update Swift4 ====================
for subView in searchBar.subviews {
for view in subView.subviews {
if view.isKind(of: NSClassFromString("UINavigationButton")!) {
let cancelButton = view as! UIButton
cancelButton.setTitleColor(.white, for: .normal)
cancelButton.setTitle("取消", for: .normal)
}
if view.isKind(of: NSClassFromString("UISearchBarBackground")!) {
let imageView = view as! UIImageView
imageView.removeFromSuperview()
}
}
}
Alternate version as an extension
extension UISearchBar {
func removeBackgroundImageView(){
if let view:UIView = self.subviews.first {
for curr in view.subviews {
guard let searchBarBackgroundClass = NSClassFromString("UISearchBarBackground") else {
return
}
if curr.isKind(of:searchBarBackgroundClass){
if let imageView = curr as? UIImageView{
imageView.removeFromSuperview()
break
}
}
}
}
}
}
In my case it helped:
searchView.backgroundImage = UIImage()
searchView.searchTextField.backgroundColor = .white
The current answers will cause runtime errors if run within iOS 13:
Terminating app due to uncaught exception 'NSGenericException', reason:
'Missing or detached view for search bar layout. The application must not remove
<UISearchBarBackground: 0x102d05050; frame = (0 0; 414 56); alpha = 0; hidden = YES;
userInteractionEnabled = NO; layer = <CALayer: 0x280287420>> from the hierarchy.'
If the code must be run by devices between iOS 9 and iOS 13, then the below is a possible solution.
First, create an extension that allows for the recursive finding of a subview based on a class name:
extension UIView {
/// Find the first subview of the specified class.
/// - Parameter className: The class name to search for.
/// - Parameter usingRecursion: True if the search should continue through the subview tree until a match is found; false otherwise
/// - Returns: The first child UIView of the specified class
func findSubview(withClassName className: String, usingRecursion: Bool) -> UIView? {
// If we can convert the class name until a class, we look for a match in the subviews of our current view
if let reflectedClass = NSClassFromString(className) {
for subview in self.subviews {
if subview.isKind(of: reflectedClass) {
return subview
}
}
}
// If recursion was specified, we'll continue into all subviews until a view is found
if usingRecursion {
for subview in self.subviews {
if let tempView = subview.findSubview(withClassName: className, usingRecursion: usingRecursion) {
return tempView
}
}
}
// If we haven't returned yet, there was no match
return nil
}
}
Then, instead of removing the subview, make it fully transparent. The backgroundColorView view is the color that shows up directly underneath the text, but adjusting it is not a necessary part of the solution.
// On iOS 9, there is still an image behind the search bar. We want to remove it.
if let backgroundView = searchBar.findSubview(withClassName: "UISearchBarBackground", usingRecursion: true) {
backgroundView.alpha = 0
}
// The color on iOS 9 is white. This mimics the newer appearance of the post-iOS 9
// search controllers
if let backgroundColorView = searchBar.findSubview(withClassName: "_UISearchBarSearchFieldBackgroundView", usingRecursion: true) as? UIImageView {
backgroundColorView.backgroundColor = UIColor.lightGray
backgroundColorView.layer.cornerRadius = 8
backgroundColorView.alpha = 0.3
backgroundColorView.image = nil
}
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)