Find UILabel in UIView in Swift - ios

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)

Related

How to identify each UIView uniquely?

I have a class like this:
class foo: UIView
{
static let childView: UIView
static func addView(onView: UIView)
{
//add childView onto onView
}
static func removeView(onView: UIView)
{
//remove childView from onView
}
}
I want to identify each onView separately, i.e.
If I do like this in a viewController
foo.addView(onView: self.view)
foo.addView(onView: self.view)
It should not remove childView only by calling removeView(onView: self.view) once, in-fact it should also call like this in order to remove child view
removeView(onView: self.view)
removeView(onView: self.view)
For this purpose, I need to identify each UIView separately.
How to identify each onView separately, and the number of times the child view add function is called for it ?
p.s I had tried this with accessibilityIdentifier and set a string tag when add view function is called, and increase the tag if onView has an associated tag with it. And in remove view, I am removing the child view, only if the tag value goes 0.
This somehow achieved what I want, but I am doubting on accessibilityIdentifier, will this disturb some built in functionality.
what apple says is: Here
But don't know what UI Automation Interfaces is, and what is accessibility label.
Edit: (Code inside functions)
static func addView(onView: UIView)
{
if let iden = onView.accessibilityIdentifier,
let identifier = Int(iden)
{
if identifier == 0
{
onView.accessibilityIdentifier = "1"
showHud(onView: onView)
}
else
{
onView.accessibilityIdentifier = "\(identifier + 1)"
}
}
else
{
onView.accessibilityIdentifier = "1"
showHud(onView: onView)
}
}
static func removeView(fromView: UIView)
{
if let iden = fromView.accessibilityIdentifier,
let identifier = Int(iden)
{
if identifier == 1
{
fromView.accessibilityIdentifier = "0"
childView.removeFromSuperview()
}
else
{
if identifier > 1
{
fromView.accessibilityIdentifier = "\(identifier - 1)"
}
}
}
else
{
fromView.accessibilityIdentifier = "0"
childView.removeFromSuperview()
}
}

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 I refresh the contents of a control in a UIStackView?

In Swift 3 (XCode 8.3.3) I have a control in a UIStackView. I have an array of UIImageViews, and loop through the array to populate the stack view at run time:
for voiceIcon in voiceIcons {
let voiceView = UIImageView(image: voiceIcon)
addArrangedSubview(voiceView)
}
These icons will sometimes become disabled (replaced with a new image), so in order to update the control, I have a function to remove all the icons so that I can re-add the appropriate ones (if there's a better way, I'm listening!):
private func resetIconsView() {
for subUIView in self.subviews as [UIView] {
removeArrangedSubview(subUIView)
subUIView.removeFromSuperview()
print("Removing")
}
}
I've also tried
for subUIView in self.subviews as! [UIImageView] { ... }
I get the debug line "Removing" for each of the icons, but they still remain in the control and the UI. I'm new to Swift, so I'm likely not understanding something, what approach should I take?
Try code below:
for view in arrangedSubviews {
view.removeFromSuperview()
}
I am assuming your UIStackView only contains some UIImageView. You can iterate through all the arranged subviews of your stack view and update your image of that imageView. A sample implementation could look like below:
func changeImage() {
for view in self.arrangedSubviews {
if let imgView = view as? UIImageView {
imgView.image = UIImage(named: "taka_icon.png")
}
}
}
I did it with an extension. You have also to remove Constraints if existing. Else this can cause some trouble.
USAGE: myStackView.removeAllArrangedSubviews()
public extension UIStackView {
func removeAllArrangedSubviews() {
let removedSubviews = arrangedSubviews.reduce([]) { (allSubviews, subview) -> [UIView] in
self.removeArrangedSubview(subview)
return allSubviews + [subview]
}
// Deactivate all constraints
NSLayoutConstraint.deactivate(removedSubviews.flatMap({ $0.constraints }))
// Remove the views from self
removedSubviews.forEach({ $0.removeFromSuperview() })
}
}

Trying to loop through UIView and all subviews to get the first responder, not working

I am trying to find the current focused First Responder by looping through the main UIView and all of it's SubViews, and all of it's SubViews through recursion, but I am coming up with nil.
extension UIView {
func getCurrentFirstResponder() -> AnyObject? {
if self.isFirstResponder() {
return self
}
for subView: UIView in self.subviews as [UIView] {
if subView.isFirstResponder() {
return subView
}
else {
subView.getCurrentFirstResponder()
}
}
return nil
}
}
let focusedView = self.view.getCurrentFirstResponder() as? UIView
Does this look correct? Why am I getting a nil view when I use this?
You code doesn't return anything in case the recursive call to subView.getCurrentFirstResponder() actually finds a first responder.
Try this:
for subView: UIView in self.subviews as [UIView] {
if subView.isFirstResponder() {
return subView
}
else {
if let sub = subView.getCurrentFirstResponder() {
return sub;
}
}
}
return nil

Loop through subview to check for empty UITextField - Swift

I"m wondering how to essentially transform the objective c code below into swift.
This will loop through all the subviews on my desired view, check if they are textfields, and then check if they are empty of not.
for (UIView *view in contentVw.subviews) {
NSLog(#"%#", view);
if ([view isKindOfClass:[UITextField class]]) {
UITextField *textfield = (UITextField *)view;
if (([textfield.text isEqualToString:""])) {
//show error
return;
}
}
}
Here is where i am with swift translation so far:
for view in self.view.subviews as [UIView] {
if view.isKindOfClass(UITextField) {
//...
}
}
Any help would be great!
Update for Swift 2 (and later): As of Swift 2/Xcode 7 this can be simplified.
Due to the Objective-C "lightweight generics", self.view.subviews
is already declared as [UIView] in Swift, therefore the cast
is not necessary anymore.
Enumeration and optional cast can be combined with to a for-loop
with a case-pattern.
This gives:
for case let textField as UITextField in self.view.subviews {
if textField.text == "" {
// show error
return
}
}
Old answer for Swift 1.2:
In Swift this is nicely done with the optional downcast operator as?:
for view in self.view.subviews as! [UIView] {
if let textField = view as? UITextField {
if textField.text == "" {
// show error
return
}
}
}
See "Downcasting"
in the Swift book.
Swift 5 and Swift 4: -
A Very simple answer you can understand easyly : - You can handle all kind of Objects like UILable, UITextfields, UIButtons, UIView, UIImages . any kind of objecs etc.
for subview in self.view.subviews
{
if subview is UITextField
{
//MARK: - if the sub view is UITextField you can handle here
if subview.text == ""
{
//MARK:- Handle your code
}
}
if subview is UIImageView
{
//MARK: - check image
if subview.image == nil
{
//Show or use your code here
}
}
}
//MARK:- You can use it any where, where you need it
//Suppose i need it in didload function we can use it and work it what do you need
override func viewDidLoad() {
super.viewDidLoad()
for subview in self.view.subviews
{
if subview is UITextField
{
//MARK: - if the sub view is UITextField you can handle here
if subview.text == ""
{
//MARK:- Handle your code
}
}
if subview is UIImageView
{
//MARK: - check image
if subview.image == nil
{
//Show or use your code here
}
}
}
}

Resources