I have a blue line in my app that displays an artificial horizon.
I want to be able to display and remove it at different parts of the code (.m).
Can I declare it (lineViewHorizon) universally.
At present I can make it appear/disappear only within the same section/method of code.
I presume this can be done?
UIView *lineViewHorizon = [[UIView alloc] initWithFrame:CGRectMake(0, pageTopMargin+inthorizon, self.view.bounds.size.width, 2)];
lineViewHorizon.backgroundColor = [UIColor blueColor];
[self.view addSubview:lineViewHorizon];
[lineViewHorizon removeFromSuperview];
I want to be able to display and remove it at different parts of the code
In order to remove a view from another view, you'll need a pointer to the view that you want to remove. You can get that either by finding the view in the view hierarchy, perhaps using -viewWithTag:, or you can keep a pointer to the view in an instance variable (or property). Either way, the key thing is that you need a pointer to the view so that you can send it a -removeFromSuperview message.
Related
Usually I do all of the creation of views in storyboard but now I need to add a view via code and although I understand the very basics of doing one, I need some help on how I go about setting up views but more than that I need to get them placed inside and existing view and also create multiple items. So I will have a parent UIScrollView and inside of that one I need to be able to create up to x additional blocks each block containing two lables and one textview. Something along these lines
I can use the storyboard to create the Parent ScrollView
ParentSCROLLVIEW
child_1_block
lable_1_1
label_1_2
textview_1
child_2_block
lable_2_1
label_2_2
textview_2
child_3_block
lable_3_1
label_3_2
textview_3
child_4_block
lable_4_1
label_4_2
textview_4
.
.
.
child_x_block
lable_x_1
label_x_2
textview_x
I am trying to create just a single one now but I am not sure of the syntax to place it inside of an existing scrollview currently it is going into the main view working on that right now. The other confusing this I have been thinking about is teh creation of a dynamic amount of the same type of objects are is the object name determined if it is going to be dynamic
UITextView *newTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, size.height + 16)];
Currently I use this to create a new textview but I will need to do the same thing for multiple entities
such as *newTextView1, *newTextView2, *newTextView3, *newTextView4, ..., *newTextViewX
Am I able to construct a string with the appended number and then use that string as the name of the object I need to create...never did that before so I am not sure but I have a feeling I would see errors
I am hoping some one could show me some sample code or point me in the right direction or even suggest what the correct terms I can search for...anything would be helpful
Jeff
You can't give dynamic names to instances.
What you can do, is give them different tags, in case they subclassing from UIView (Like UITextView).
Run this in a loop:
UITextView *myTextView = [[UITextView alloc] initWithFrame....];
[self.view addSubview:myTextView];
myTextView.tag = loopIndexInt; // This is where you put your dynamic number.
Now, In order to retrieve a specific UITextView by it's tag, do:
UITextView *textView = [self.view viewWithTag:5]; // in order to get textView with tag number 5.
Anyway, it really depends on what you're trying to do.
In my app I have two screens - first to show a user profile, the second - to edit the profile information. They are similar. I have completed the xib file for the first screen.
What's the best way to reuse it on second screen?
You should encapsulate the related elements as a custom view class. You can tackle this problem by creating views with code instead of just xibs, and I would recommend this.
But, if you would prefer to use a xib, you can create one that models the stuff you want to reuse. And then in your view controller call some code like this:
UIView* aView = [UIView alloc] initWithFrame .....];
[[NSBundle mainBundle] loadNibNamed:#"MyReusableComponent" owner:aView options:nil];
UILabel* someLabel = aView.injectedLabel; //this is alive after loading the xib
[self.view addSubView:aView];
When you create your xib, your need to set the Files Owner to a class that will respond to the setters for the properties that will be injected. (Eg your new view class). This way you can wire up the references.
For more information, look at Apple's examples of loading table cells from a xib - this is the same technique. When you load a xib and specify the owner, it will inject the values from the xib into the owner, in this case a custom view.
Dou you mean that you enter the view controller's edit mode and reuse those those elements you have created ?
Enabling Edit Mode in a View Controller
You could use UITextFields (instead of UILabels you may have logically used for show) that you change in appearance, and switch enabled on/off. As a minimal example:
Show:
self.textField.borderStyle = UITextBorderStyleNone;
self.textfield.enabled = NO;
Edit:
self.textField.borderStyle = UITextBorderStyleBezel;
self.textfield.enabled = YES;
You could of course do more on appearance, than just these basics.
In Xcode: Go to file > duplicate.
Then name your duplicated xib something like "editProfile" This will give a duplicate of your first xib that you can adjust as necessary
I am trying to execute following code in viewDidLoad method of my single view controller project:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
But it does not give the desired inset. However other UI changes i make in the same method do work e.g
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
Above line of code does work and background is change to orange but the frame does not.
The inset works only if I place the line of code in viewDidAppear. I would like to understand the key reason for this behavior if anyone can explain. Thank you in advance.
I think the issue you are running into with this line:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
can be explained like so :
in viewDidLoad, the properties are set, but the frame of the views are not yet defined.
by the time viewWillAppear is triggered, they will be set. That explains why that line of code works there.
But since iOS 5, there is another method called after viewDidLoad and before viewWillAppear in which the view frames are set : viewDidLayoutSubviews.
You can find the complete explanation of this in this season's Stanford CS193P course about iOS programming (very cool by the way).
So if you want it to work just once, use :
- (void)viewDidLayoutSubviews
{
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
}
PS : I posted this answer on Ray's forum too.
Regards,
Fred
The viewDidLoad method is too early to set your view's frame because something else is changing the frame later.
For example, if this is your root view controller, then the UIWindow object will set the view's frame when it adds the view to itself as a subview. The viewDidLoad method runs as soon as loadView returns, before any outside object (like the UIWindow) can get its hands on the view.
You can test this by using a custom view (if you're not already) and overriding setFrame: in the custom view class:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
}
Put a breakpoint in that method and you'll see that the view's frame gets set some time after viewDidLoad returns.
Rob Mayoff's answer is correct and excellent, but putting it a slightly different way: viewDidLoad only means the view has loaded, i.e. that the view controller has obtained its view. It doesn't mean that the view has been placed in the interface. That, indeed, is one of the things that viewDidAppear: does mean — and that's why it worked when you ran your code there.
The trick in this sort of situation, where you want to initialize something about the view, is to do it late enough but do it only once. viewDidAppear: could easily be called again later, but you don't want to initialize the view again (unless it has been unloaded). In iOS 5, isMovingToParentViewController allows you to distinguish the particular circumstances you're looking for. Before that, it might be necessary to set up a BOOL flag so that you perform final initializations only once.
A related trap is what happens when the app launches into landscape orientation. Here, too, viewDidLoad is too soon because the interface has not yet rotated into landscape.
However, this issue should not be arising at all. It should be none of your business to inset a view controller's view. Either the view is the root view controller, in which case its size is correctly taken care of automatically, or it is the child of a parent view controller, in which case it is the parent view controller's job to size the view (as UINavigationController, for example, already does), or the view is to be presented modally, in which case its size will be set automatically to match the view it replaces. So I would suggest that you very question suggests you're doing something wrong.
Create your project with an older version of Xcode (for instance I'm using Xcode 4.3.3 for this) . Then you can use setFrame: method with viewDidLoad in any version of Xcode .
I also experienced this issue on Ray Wenderlich's tutorial 'Introduction to CALayers'.
http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial
It seems shrinking the entire view controllers view is not the best thing to do. Instead create a sub view and call CGRectInset on that e.g in your view controllers viewDidLoad
UIView *viewToManipulate = [[UIView alloc] initWithFrame:CGRectMake(0.0,0.0, self.view.bounds.size.width, self.view.bounds.size.height)];
[self.view addSubview:viewToManipulate];
viewToManipulate.backgroundColor = [UIColor redColor];
viewToManipulate.layer.cornerRadius = 20.0;
viewToManipulate.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
I have the following items in logElementTree output:
UIAButton: rect:{{20, 427}, {41, 41}}
UIAButton: rect:{{140, 427}, {41, 41}}
These buttons have no identifier, no name, and are not drawn in XIB. On my automation test script I only use index (something like target.frontMostApp().mainWindow().buttons()[7].tap())
But then, this line will not always work because index is changing. I just want to ask, if there's a way to tap this button other than using index? Please note that the button has no name, so I cannot use buttons()["name'"].tap()
Technically this would be the best way to go about doing what you would like, so I'll leave it here for other developers who see this question. In your case since you have limited technical experience, I would recommend asking your developer to assign ids or names to buttons. Providing good names for buttons and other UI elements means that your app is also accessible for those users with impaired eyesight, since voiceover will read them the names you've given to your buttons. The following block of code will programmatically assign a label and a accessibility hint to a dynamically created object that conforms to UIAccessibility.
From Apple's documentation (in this case they're doing it on a view, but you could do it on any object like a button):
- (id)init
{
_view = [[[MyCustomView alloc] initWithFrame:CGRectZero] autorelease];
[_view setIsAccessibilityElement:YES];
[_view setAccessibilityTraits:UIAccessibilityTraitButton];
[_view setAccessibilityLabel:NSLocalizedString(#"view.label", nil)];
[_view setAccessibilityHint:NSLocalizedString(#"view.hint", nil)];
}
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html#//apple_ref/doc/uid/TP40008785-CH102-SW5
Javascript "hack" (not very clean, but it works...):
var window = UIATarget.localTarget();
window.tap({x:yourXCoordinateHere , y:yourYCoordinateHere});
I'm playing around with drawing in iOS apps. I have a class that is a subclass of UIView that draws some lines and stuff. When the user presses a button, I instantiate the class and do an addSubView on the view of the main UIViewController of the app. The more times the user presses that button, the more instances of that class get added to the view. It's working just fine.
Now I want to provide the user a way to delete one of those views. So far I've put a [self removeViewFromSuperview] into the touchesBegan method of the custom UIView. So when the user presses the drawing it gets removed from the view. But, it's not actually deleted, right? Since the view was instantiated within the method that executes when the button is pressed I have no way to reference it from within the UIViewController. What's the appropriate way to make sure I'm not wasting memory with a UIView that was created and removed?
On a related note, if I was to put a toggle switch on the main window's UIView that toggles delete, how can I check from within touchesBegan if that toggle switch is set to delete=yes? Would I have a some sort of boolean variable in the AppDelegate that I can check from within the UIView subclass? How would I reference that?
Thank you for your help,
Stateful
If you add the view like this:
UIView *viewBeingAdded = [[[UIView alloc] init] autorelease];
[view addSubview:viewBeingAdded];
You can remove it without leaking memory:
[theViewAboutToBeRemoved removeFromSuperview];
Regarding the UISwitch, you don't need to keep its value anywhere unless you need it for something else. You can access its value directly:
if ([theSwitch isOn]) { ... }
You don't even need an IBOutlet, you can access the switch with its tag:
UISwitch *theSwitch = (UISwitch *)[view viewWithTag:<# switch tag number #>];
if ([theSwitch isOn]) { ... }
In this case you must set a unique tag number for the switch in Interface Builder or when you create it.
When you do [mainView addSubView:myView], mainView will retain myView. If you created myView with alloc/init, then you retained it also. If you don't need myView after adding it to the main view then simply do [myView release] after adding it. When you remove it from the main view, it will get released and deallocated.
If you create the UIView with alloc/init, add it to the superview then release the view, the superview will retain it. When it is removed with removeViewFromSuperview it will be dealloc'ed.
I typically autorelease a view after adding it, leaving the parent the only reference.
As to checking a toggle, you could add an IBOutlet so you can inspect it directly. (This may not be pure MVC, but I don't know if putting it in [UIApplication sharedApplication].delegate is necessarily cleaner.)