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
}
}
}
}
Related
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()
}
}
with the new iOS 13, i got a crash trying to change the UISearchBar textField properties using valueForKey:#"_searchField"
Now seems that Apple has changed something.
I've created a subclass of UIView with the following custom method and now it seems to work!
- (UIView *)findSubview:(NSString *)name resursion:(BOOL)resursion
{
Class class = NSClassFromString(name);
for (UIView *subview in self.subviews) {
if ([subview isKindOfClass:class]) {
return subview;
}
}
if (resursion) {
for (UIView *subview in self.subviews) {
UIView *tempView = [subview findSubview:name resursion:resursion];
if (tempView) {
return tempView;
}
}
}
return nil;
}
You can simply call this method this way to change UITextField properties:
UITextField *textField = (UITextField*)[self findSubview:#"UITextField" resursion:YES];
Obviously this is an Objective-c snippet and if anyone knows how to write the same code in swift can add it to the answers.
Happy coding!
I'm not sure if it would help, but UISearchBar has a new searchTextField property allowing you to access its UISearchTextField and, in turn, its UITextField:
let searchBar = UISearchBar()
var searchField : UITextField
if #available(iOS 13.0, *) {
searchField = searchBar.searchTextField
} else {
searchField = //Your original method
}
You can do it by using below extension
extension UISearchBar {
func getAllSubview<T : UIView>(type : T.Type) -> [T]{
var all = [T]()
func getSubview(view: UIView) {
if let aView = view as? T{
all.append(aView)
}
guard view.subviews.count>0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
Use like:
self.searchBar.getAllSubview(type: UITextField.self).first
Output:
<UISearchBarTextField: 0x7fc68d850a00; frame = (0 0; 0 0); text = ''; opaque = NO; layer = <CALayer: 0x600000d29aa0>>
My project is in Objective c and i need to support XCode10 as well so, After two days of headache below line saved my day :
txfSearchField = [_searchBar valueForKey:#"searchField"];
Just need to Remove _ from the old code!!!
In Swift also you can use the same.
Hope it will help someone!
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
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)
I have found a good answer about this problem, look at here How to hide iOS7 UINavigationBar 1px bottom line
but i want to know how to implement it with swift, i've tried in this way
func findHairlineImageViewUnder(view:UIView!) {
if view is UIImageView && view.bounds.size.height <= 1.0 {
return view
}
var subview: UIView
for subview in view.subviews {
var imageView:UIImageView = self.findHairlineImageViewUnder(subview)
if imageView {
return imageView
}
}
}
i can't make it because the compiler told me
'UIView' is not convertible to ()
cannot convert the expression's type 'AnyObject' to type 'UIImageView'
Type 'UIImageView' does not conform to protocol 'BoooleanType'
i know why these errors came out but how can i fix it?
This extension should do it.
extension UINavigationController {
func hairLine(hide hide: Bool) {
//hides hairline at the bottom of the navigationbar
for subview in self.navigationBar.subviews {
if subview.isKindOfClass(UIImageView) {
for hairline in subview.subviews {
if hairline.isKindOfClass(UIImageView) && hairline.bounds.height <= 1.0 {
hairline.hidden = hide
}
}
}
}
}
}
Just call it like this:
navigationController?.hairLine(hide: true)
There are a few problems in your code.
You haven't specified a return type, so the compiler is trying to figure it out for you but is having trouble. You should specify that your return type is UIView?; it should be an optional because you may not be able to find the view and will need to return nil.
You don't need to declare subview before your for loop; the for loop will do it implicitly.
Declaring imageView to a UIImageView will not work without casting. However, you don't really need to make it a UIImageView in the first place and you can let swift deal with it.
By making the change in 1, you can now simplify the inside of your for loop using optional binding and just do if let foundView = self.findHairlineImageViewUnder(subview) { ... }
If you never find the view you're looking for, you never return anything from the function. That's going to cause a compile error in swift, so make sure to return nil at the very end.
Here is a working implementation with the above fixes:
func findHairlineImageViewUnder(view:UIView!) -> UIView? {
if view is UIImageView && view.bounds.size.height <= 1.0 {
return view
}
for subview in view.subviews as [UIView] {
if let foundView = self.findHairlineImageViewUnder(subview) {
return foundView
}
}
return nil
}