I have a ViewController and some label and textfield for user to input, and in some point I need to loop over all textfield to collect the user's input.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
for views in self.view.subviews {
if view is UITextField
{
println("inside")
}
println(views.description)
}
}
For this code, the "inside" never print out. But I do see some the information of views.description
<UILabel: 0x7fa4b1c7bd00; frame = (16 97; 76 21); text = 'FromDate'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa4b1c44580>>
<UILabel: 0x7fa4b1c9fd60; frame = (16 169; 82 21); text = 'ControlNO'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa4b1c77fb0>>
>
<UITextField: 0x7fa4b1c7a050; frame = (106 95; 197 30); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fa4b1c6c800>>
<UITextField: 0x7fa4b1c9c060; frame = (106 126; 197 30); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; tag = 1; layer = <CALayer: 0x7fa4b1c9c2c0>>
<_UILayoutGuide: 0x7fa4b1d11d20; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7fa4b1d11e00>>
<_UILayoutGuide: 0x7fa4b1d253f0; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7fa4b1d12f60>>
but if I println(views.dynamicType) I could only see ExistentialMetatype.
What is wrong in my code?
The loop variable is called views, but you test view (which is
not the loop variable and therefore identical to self.view):
for views in self.view.subviews { // "views" here
if view is UITextField { // "view" here
println("inside")
}
}
If you rename the loop variable to view
for view in self.view.subviews {
if view is UITextField {
println("inside")
}
}
then everything works as expected.
You can use isKindOfClass:
var tex = UITextField()
if tex.isKindOfClass(UITextField){
println("yes")
}
#Christian's code will work. Here is another solution.
if let view = tex as? UITextField {
// view is a UITextField
}
Related
I have several UIView layers in one ViewController.
How can I determine the order of UIView programmatically?
I found the solution...
In the UIView documentation, where are several methods listed for the manipulation of the order of subviews:
bringSubviewToFront(_:)
sendSubviewToBack(_:)
removeFromSuperview()
insertSubview(_:atIndex:)
insertSubview(_:aboveSubview:)
insertSubview(_:belowSubview:)
exchangeSubviewAtIndex(_:withSubviewAtIndex:)
In Swift 4.0
view.sendSubview(toBack:yourUIView)
view.bringSubview(toFront:yourUIView)
Views are ordered from back-most to front-most in view.subviews array.
For example, here are 3 subview added to an empty view.
view.addSubview(UILabel())
view.addSubview(UIButton())
view.addSubview(UITextField())
Printing the subviews with the following code
for view in view.subviews {
print(view.debugDescription)
}
has the output
<UILabel: 0x7feef00042e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400008c210>>
<UIButton: 0x7feef00045c0; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x604000222300>>
<UITextField: 0x7feeef02c800; frame = (0 0; 0 0); text = ''; opaque = NO; gestureRecognizers = <NSArray: 0x60800005b090>; layer = <CALayer: 0x604000222280>>
When the views are reordered, the position in view.subviews changes
if let button = view.subviews.first(where: { $0 is UIButton }) {
view.bringSubview(toFront: button)
}
Now, printing the view, has the output
<UILabel: 0x7feef00042e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400008c210>>
<UITextField: 0x7feeef02c800; frame = (0 0; 0 0); text = ''; opaque = NO; gestureRecognizers = <NSArray: 0x60800005b090>; layer = <CALayer: 0x604000222280>>
<UIButton: 0x7feef00045c0; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x604000222300>>
I am trying to loop all my subviews and print them in NSLog.
I have developed this recursive method:
- (void)allSubViews:(UIView*)mainV
{
for (UIView *view in mainV.subviews) {
NSLog(#"%#", view);
if ([[view subviews]count]>0) {
[self allSubViews:view];
}
}
}
which I call from MainViewController.m:
[self allSubViews:self.myView];
The result is:
2015-09-12 11:18:48.919 Profile-Statistics[3153:506562] <_UILayoutGuide: 0x7fc70bf1c330; frame = (0 0; 0 0); hidden = YES;
layer = >
2015-09-12 11:18:48.919 Profile-Statistics[3153:506562] <_UILayoutGuide: 0x7fc70bf1ced0; frame = (0 0; 0 0); hidden = YES;
layer = >
My View has Top and Bottom Layout Guide and a View which has 3 UIButtons, 1 ImageView and 1 UIView.
Did I miss something here?
Update:
Screenshot of StoryBoard
For what it's worth, I tried this in a sample project with a bunch of subviews and it worked as expected.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)printButtonTapped:(id)sender {
[self allSubviews:self.view];
}
- (void) allSubviews:(UIView*) parent {
NSLog(#"%#", parent);
for (UIView *child in parent.subviews) {
[self allSubviews:child];
}
}
#end
Console output is:
2015-09-13 22:06:00.904 RecursivePrint[66243:3455899] <UIView: 0x7fa902e0e6e0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fa902e15030>>
2015-09-13 22:06:00.905 RecursivePrint[66243:3455899] <UIView: 0x7fa902e0e840; frame = (70 56; 452 337); autoresize = RM+BM; layer = <CALayer: 0x7fa902e15050>>
2015-09-13 22:06:00.905 RecursivePrint[66243:3455899] <UIView: 0x7fa902e24c90; frame = (31 33; 153 160); autoresize = RM+BM; layer = <CALayer: 0x7fa902e21f00>>
2015-09-13 22:06:00.906 RecursivePrint[66243:3455899] <UIView: 0x7fa902e24fc0; frame = (219 41; 153 160); autoresize = RM+BM; layer = <CALayer: 0x7fa902e1ec10>>
2015-09-13 22:06:00.906 RecursivePrint[66243:3455899] <UIButton: 0x7fa902d177e0; frame = (164.5 603; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fa902d16c80>>
2015-09-13 22:06:00.907 RecursivePrint[66243:3455899] <UIButtonLabel: 0x7fa902d1f010; frame = (0 6; 46 18); text = 'Button'; alpha = 0.2; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa902d1f670>>
2015-09-13 22:06:00.907 RecursivePrint[66243:3455899] <_UILayoutGuide: 0x7fa902e25ed0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7fa902e21b20>>
2015-09-13 22:06:00.907 RecursivePrint[66243:3455899] <_UILayoutGuide: 0x7fa902f77ec0; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x7fa902f09f70>>
As a side note, this is the first time in over a year that I've tried to do something in Objective-C. The Swift version seems so much easier to understand:
import UIKit
class ViewController: UIViewController {
#IBAction func printButtonTapped(sender: UIButton) {
printSubviews(self.view)
}
}
func printSubviews(parent:UIView) {
print(parent)
for child in parent.subviews {
printSubviews(child)
}
}
In addition, the Swift version of printSubviews is defined as a function, not a method so you don't call it from self, you just call it with the top level view you want to start with.
We have a login screen with a UILabel hidden initially to represent a message when the user logs out of the app.
When VoiceOver is turned on in iOS and the user tries to log out from the app, the voice over should ideally read out the logout message label. Instead, it reads out the password text field of the login screen.
The action of the log out button has the below implementation code.
let loginStoryboard = UIStoryboard(name: "Login", bundle: nil)
let loginViewController = loginStoryboard.instantiateInitialViewController() as! LoginViewController
loginViewController.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
loginViewController.logOut = true
self.presentViewController(loginViewController, animated: true, completion:nil)
The logout indicator is set to display the logout message label.
LoginViewController viewDidLoad code.
if(!logOut){
self.logOutMsg.hidden = true
}else{
self.logOutMsg.text = NSLocalizedString("LoggedOutMsg", comment: "Logged out message")
self.logOutMsg.hidden = false
}
The login screen fields are accessibility enabled in story board.
The behavior is inconsistent: sometimes the logout message label is read and sometimes it reads out the password text field.
Whenever VoiceOver reads the password text field, I can see an error in the console log.
|error| Could not find <UIWindow: 0x124d13b10; frame = (0 0; 768 1024); gestureRecognizers = <NSArray: 0x174241140>; layer = <UIWindowLayer: 0x1742319c0>> in a list of sorted view [parent: <CaseworkerApp.AppDelegate: 0x124e008e0>] siblings (
"<UILabel: 0x124d06cf0; frame = (132 1; 300 18); text = 'You are logged out of IBM...'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1742921b0>>",
"<CaseworkerApp.LoginTextField: 0x124da4890; baseClass = UITextField; frame = (80 37; 330 50); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x1704551e0>; layer = <CALayer: 0x175033de0>>",
"<CaseworkerApp.LoginTextField: 0x124d9bd50; baseClass = UITextField; frame = (80 110; 330 50); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; tag = 1; gestureRecognizers = <NSArray: 0x17044dfb0>; layer = <CALayer: 0x175227ce0>>",
"<UIButton: 0x124d52900; frame = (80 183; 330 50); autoresize = RM+BM; layer = <CALayer: 0x175039ae0>>"
). If this happened right around a screen change, it might be okay, but otherwise this is probably a bug.
2015-08-10 16:46:50.108 CaseworkerApp[2217:479225] |error| Could not find <UIWindow: 0x124d13b10; frame = (0 0; 768 1024); gestureRecognizers = <NSArray: 0x174241140>; layer = <UIWindowLayer: 0x1742319c0>> in a list of sorted view [parent: <CaseworkerApp.AppDelegate: 0x124e008e0>] siblings (
"<UILabel: 0x124d06cf0; frame = (132 1; 300 18); text = 'You are logged out of IBM...'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1742921b0>>",
"<CaseworkerApp.LoginTextField: 0x124da4890; baseClass = UITextField; frame = (80 37; 330 50); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x1704551e0>; layer = <CALayer: 0x175033de0>>",
"<CaseworkerApp.LoginTextField: 0x124d9bd50; baseClass = UITextField; frame = (80 110; 330 50); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; tag = 1; gestureRecognizers = <NSArray: 0x17044dfb0>; layer = <CALayer: 0x175227ce0>>",
"<UIButton: 0x124d52900; frame = (80 183; 330 50); autoresize = RM+BM; layer = <CALayer: 0x175039ae0>>"
).
If this happened right around a screen change, it might be okay, but otherwise this is probably a bug.
Any help please to resolve this?
When a view is presented, VoiceOver starts reading from the first element in the accessibility tree. There are possibly two solutions.
First, you can modify the order.
Change order of read items with VoiceOver
Secondly, you can make VoiceOver to focus on a particular element when a view changes by posting ScreenChanged Notification from UIAccessibility indicating which element VoiceOver should focuss.
UIAccessibility.post(notification:.screenChanged, argument:elementToBeFocussed)
Read more information from the documentation.
UIAccessibility.post: https://developer.apple.com/documentation/uikit/uiaccessibility/1615194-post
UIAccessibility.Notification: https://developer.apple.com/documentation/uikit/uiaccessibility/notification
screenChanged: https://developer.apple.com/documentation/uikit/uiaccessibility/notification/1620198-screenchanged
I am working on one app. Which capture images continuously, there is one button to stop capturing. But the problem is that button targeted method is not being called. Even when I clicked on the screen anywhere it didn't detect touch or gesture. I have implemented gesture and touch delegate methods to check. Capturing image is not on main thread so main thread is free. So I am not able to find the reason why view not able to detect the touch. There are almost 6-7 button on that particular view, none of them trigger their method. While working with ios7 its running properly, It will detect touch and button targeted method trigger also.
I have thought that there should be any-view that will be overlap those button so i print the stake of subview , which is as below.Button which stop capturing is with text "START". I have just share stake after START button only as i think in stake the last item will be on top in view.
<UIButtonLabel: 0x15dfb0d0; frame = (5 14; 41 16); text = 'START';
opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer:
0x15dfb190>>
<UIButton: 0x15e8c590; frame = (63 7; 36 32); opaque = NO; autoresize
= RM+BM; layer = <CALayer: 0x15e8c690>>
<UIButtonLabel: 0x15df0400; frame = (15 10; 6 12); text = '0'; opaque
= NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x15d98db0>>
<UIImageView: 0x15e97c00; frame = (-295 -272; 1557 1568);
clipsToBounds = YES; hidden = YES; autoresize = W+H;
autoresizesSubviews = NO; layer = <CALayer: 0x15ea2920>>
<_UILayoutGuide: 0x15e90ba0; frame = (0 0; 0 0); hidden = YES; layer =
<CALayer: 0x15ea2b10>>
<_UILayoutGuide: 0x15e86120; frame = (0 1024; 0 0); hidden = YES;
layer = <CALayer: 0x15e6fc40>>
<MPVolumeView: 0x15db7840; frame = (18 340; 284 23); alpha = 0; opaque
= NO; layer = <CALayer: 0x15dbd2e0>>
<UILabel: 0x15daee40; frame = (0 0; 0 0); text = 'No Volume
Available'; opaque = NO; autoresize = W+BM; userInteractionEnabled =
NO; layer = <_UILabelLayer: 0x15dd5be0>>
<MPButton: 0x15dd5e00; baseClass = UIButton; frame = (131.5 2.5; 21.5
18); alpha = 0; opaque = NO; autoresize = LM+BM; layer = <CALayer:
0x15dd6030>>
<UIImageView: 0x15de0c30; frame = (-39.25 -41; 100 100); alpha = 0;
opaque = NO; userInteractionEnabled = NO; tag = 1886548836; layer =
<CALayer: 0x15ea5330>>
<UIImageView: 0x15ea5790; frame = (0 0; 21.5 18); clipsToBounds = YES;
opaque = NO; userInteractionEnabled = NO; layer = <CALayer:
0x15ea5810>>
I am using auto-layout. Please can you help me to handle this problem. I am new with auto-layout. so anything that i can change in storyboard get this problem solved please let me know. Thanks in advance.
Thanks for all your answers. I got solution by moving irate code from +initialize method of AppDelegate to didFinishLaunchingWithOptions method. My iRate get initialized and configure but don't show popup. May be that's why i was not able to perform any touch on screen.
I add objects in UIScrollView inside cycle FOR, but after the second interaction the index of subviews change.
When i create the ScrollView:
scroll.subviews :(
"<UIImageView: 0x147aeee0; frame = (1017 186; 7 5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x147aef50>>",
"<UIImageView: 0x147aefa0; frame = (1019 184; 5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x147af010>>"
)
After i have the cycle FOR with [scroll addSubview:btn];
First
scroll.subviews :(
"<UIImageView: 0x147aeee0; frame = (1017 186; 7 5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x147aef50>>",
"<UIImageView: 0x147aefa0; frame = (1019 184; 5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x147af010>>",
"<ContentButton: 0x13fdc610; baseClass = UIButton; frame = (15 10; 221 173); opaque = NO; tag = 1; layer = <CALayer: 0x1034cc00>>"
)
Second (change last object to index 0)
scroll.subviews :(
"<ContentButton: 0x13fdc610; baseClass = UIButton; frame = (15 10; 221 173); opaque = NO; tag = 1; layer = <CALayer: 0x1034cc00>>",
"<UIImageView: 0x147aefa0; frame = (1019 184; 5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x147af010>>",
"<UIImageView: 0x147aeee0; frame = (1017 186; 7 5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x147aef50>>",
"<ContentButton: 0x105ddf70; baseClass = UIButton; frame = (246 10; 221 173); opaque = NO; tag = 149; layer = <CALayer: 0x105523e0>>"
)
Third and so on, the object will add normally (in last index)
scroll.subviews :(
"<ContentButton: 0x13fdc610; baseClass = UIButton; frame = (15 10; 221 173); opaque = NO; tag = 1; layer = <CALayer: 0x1034cc00>>",
"<UIImageView: 0x147aefa0; frame = (1019 184; 5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x147af010>>",
"<UIImageView: 0x147aeee0; frame = (1017 186; 7 5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x147aef50>>",
"<ContentButton: 0x105ddf70; baseClass = UIButton; frame = (246 10; 221 173); opaque = NO; tag = 149; layer = <CALayer: 0x105523e0>>",
"<ContentButton: 0x13f1dc80; baseClass = UIButton; frame = (477 10; 221 173); opaque = NO; tag = 3; layer = <CALayer: 0x13f1dd20>>"
)
The problem is in the second interaction, so why the last object (ContentButton) was in the index 2 and change into index 0?
for (obj * objScroll in arrayChaObj)
{
// UIButton
ContentButton * btn = [[ContentButton alloc] initWithFrame:CGRectMake(btContentPosXChaObj,
10,
imgButtonScroll.size.width,
imgButtonScroll.size.height)];
btContentPosXChaObj += 10 + imgButtonScroll.size.width;
[scroll addSubview:btn];
NSLog(#"scroll.subviews :%#",scroll.subviews);
[scroll setContentSize:CGSizeMake(btContentPosXChaObj, imgButtonScroll.size.height+10)];
}
This is just a wild guess.
What happens when you call setContentSize: for the first time? Well, the two image views end up outside the content region. They won't be ever displayed.
I guess the scrollview then optimizes the order of subviews and puts the invisible ones to the end which can probably improve its clipping (redrawing) functionality.
Currently I don't have the time to check this and experiment but it's a reasonable explanation for me.