I have a weird issue with a UIStepper (and it's accompanying UITextField)
Consider this code snippet:
#interface LTRPageTracker : UIView <UITextFieldDelegate>
{
UIStepper* page_move;
UITextField* page_no_view;
}
-(void) nextOrPrevPage:(id)sender forEvent:(UIControlEvents) event;
#implementation LTRPageTracker
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
CGRect stepperFrame, pageNoframe;
pageNoframe.origin = frame.origin;
pageNoframe.size.height = frame.size.height;
pageNoframe.size.width = frame.size.width/2;
stepperFrame.origin.x = pageNoframe.origin.x + pageNoframe.size.width +1;
stepperFrame.origin.y = frame.origin.y;
stepperFrame.size = pageNoframe.size;
page_move = [[UIStepper alloc] initWithFrame:stepperFrame];
[page_move setMinimumValue:0];
[page_move setValue:7];
page_move.maximumValue =1000;
[page_move addTarget:self action:#selector(nextOrPrevPage:forEvent:) forControlEvents:UIControlEventValueChanged];
page_no_view = [[UITextField alloc] initWithFrame:pageNoframe];
page_no_view.delegate = self;
page_no_view.backgroundColor = [UIColor redColor];
[self addSubview:page_no_view];
[self addSubview:page_move];
[page_move sendActionsForControlEvents:UIControlEventValueChanged];
[page_move setEnabled:YES];
}
return self;
}
-(void) nextOrPrevPage:(id) sender forEvent:(UIControlEvents) event {
//assert(sender == page_move);
NSLog(#"Event is %x", event);
page_no_view.text = [[NSNumber numberWithDouble: page_move.value] stringValue];
}
I have added this View to the navigation bar.
And I can decrement the value of the UIStepper but no increment it (the event will simply not get triggered for increment but will do so decrement).
WHY?
Using iOS 7, running on simulator.
Whenever you have a problem with a UI element not responding, the first thing you should check is whether it (or part of it) is outside the bounds of its superview (the part that's out of the bounds will not respond). An easy way to do this, is to give the superview a background color. In the init method assign a background color to self, and see what that shows you. I'm betting that you'll see that the right side of the stepper is not within its superview, so you'll need to make that view bigger, or change the stepper's position within that view.
If you make the view's frame 150x30, and change these two lines in the view's init method, I think it should work ok for a number as large as 1000:
pageNoframe.size.width = frame.size.width/4; // changed 2 to 4
stepperFrame.origin.x = pageNoframe.origin.x + pageNoframe.size.width+20; // changed 1 to 20
Related
I am adding controls to a ViewController by code (not XIB).
Form has two UITextfield controls which are defined as IBOutlet in the .h file
#interface ConditionsViewController : UIViewController <UITabBarDelegate, UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource>
{
id tfDelegate;
IBOutlet UITextField *tfOAT;
IBOutlet UITextField *tfWind;
}
- (void) setTextFieldAttributes:(UITextField *)tf;
...
In the .m within viewDidLoad, both UITextField are init and alloc
[super viewDidLoad];
tfDelegate = self;
//create OAT textfield
frame = CGRectMake( xRightSide-boxWidth*1.5-5, 200, boxWidth*0.75, boxHeight );
tfOAT = [[UITextField alloc] initWithFrame:frame];
[tfOAT setFont:font];
[self setTextFieldAttributes:tfOAT];
[[self view] addSubview:tfOAT];
NSLog(#"OAT after addSubview is: %#",tfOAT);
//create Wind textfield
frame = CGRectMake( xRightSide-boxWidth*1.5-5, 400, boxWidth*0.75, boxHeight );
UITextField *tfWind = [[UITextField alloc] initWithFrame:frame];
[tfWind setFont:font];
[self setTextFieldAttributes:tfWind];
//[tfWind setText:#"1"];
[[self view] addSubview:tfWind];
NSLog(#"WindField after addSubview is: %#",tfWind);
In the viewWillAppear
// OAT is returned
float t = [pModel OAT]; //t returns 12
if( t == 0 )
[tfOAT setText:#""];
else
[tfOAT setText:[NSString stringWithFormat:#"%.0f", t]];
NSLog(#"OAT within viewWillAppear is: %#",tfOAT);
NSLog(#"OAT text is: %#",tfOAT.text);
// Wind component is returned
int w = [pModel wind]; //w does return an integer 8
if(w==0)
[tfWind setText:#"0"];
else
[tfWind setText:[NSString stringWithFormat:#"%d",w]];
NSLog(#"WindField in viewDidAppear: %#",tfWind);
NSLog(#"Windfield text is: %#",tfWind.text);
Here is the setTextAttributes function, doesn't do anything special.
- (void) setTextFieldAttributes:(UITextField *)tf
{
tf.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
tf.borderStyle = UITextBorderStyleRoundedRect;
[tf setDelegate:self];
tf.clearsOnBeginEditing = YES;
[tf setKeyboardType:UIKeyboardTypeNumbersAndPunctuation];
[tf setReturnKeyType:UIReturnKeyDone];
}
The NSLog prints out as such. Note that the objects have pointer/reference numbers 0x7fccad7219a0 and 0x7fccb041beb0.
//NSLogs within viewDidLoad
OAT after addSubview is: <UITextField: 0x7fccad7219a0; frame = (510.5 200; 71.25 55); text = ''; opaque = NO; layer = <CALayer: 0x60000328f0e0>>
WindField after addSubview is: <UITextField: 0x7fccb041beb0; frame = (510.5 400; 71.25 55); text = ''; opaque = NO; layer = <CALayer: 0x600003284860>>
//NSLogs within viewWillAppear
OAT within viewWillAppear is: <UITextField: 0x7fccad7219a0; frame = (510.5 200; 71.25 55); text = '12'; opaque = NO; tag = 3; layer = <CALayer: 0x60000328f0e0>>
OAT text is:12
WindField within viewDidAppear: (null)
Windfield text is: (null)
Note that the pointer or reference to tfWind has vanished somewhere between viewDidLoad and viewWillAppear. In the navigator window under the tfWind object, the entry _delegate = (id) 0x0 appears after a break in viewWillAppear but shows the reference number for tfOAT. Both controls are still on the screen but tfWind has become "disconnected".
If I uncomment the //[tfWind setText:#"1"]; line in the viewDidLoad (restart the app), I indeed do get the "1" to appear within the textfield.
This is a real mystery. Anyone have any ideas or seen this before?
Ah...
The reason it has "become disconnected" is because you are creating a new variable in viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
// creates a text field object for the class property tfOAT
tfOAT = [[UITextField alloc] initWithFrame:frame];
...
// creates a NEW LOCAL text field object
UITextField *tfWind = [[UITextField alloc] initWithFrame:frame];
...
// end of viewDidLoad, the LOCAL tfWind goes out-of-scope
}
See the difference?
I am sublassing either an UIImageView or UIView for generating simple color tiles with digits. If I am using UIImageView, I use initWithImage method. If I use UIView, the method initWithFrame is being used.
Either red square image or programmatically generated red view is used for initialization.
The problem can be seen on two screenshots: when initWithImage is being used - everything works fine. If initWithFrame method is being used, I am ending with multiple white views without any information created in even order with normal views. All the screenshots and code attached.
This how it looks when initializing using initWithImage:
- (id)initWithImage:(UIImage *)image {
self = [super initWithImage:image];
if (self) {
//Some non-important, label-related stuff.
[self addSubview:self.numberLabel];
}
return self;
}
And this is how it looks with initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.redView = [self redViewWithFrame:frame];
[self addSubview:self.redView];
//label-related stuff
}
return self;
}
- (UIView *)redViewWithFrame:(CGRect)frame {
UIView *view = [[UIView alloc] initWithFrame:frame];
view.backgroundColor = [UIColor redColor];
view.alpha = 1;
return view;
}
And the for-loop, calling these initializers from another class (UIScrollView subclass). The label value is being set after the view had been initialized.
- (void)setNumberOfBlocks:(NSInteger)numberOfBlocks {
_blocks = [[NSMutableArray alloc] init];
CGSize contentSize;
contentSize.width = BLOCK_WIDTH * (numberOfBlocks);
contentSize.height = BLOCK_HEIGHT;
self.contentSize = contentSize;
for (int i = 0; i < numberOfBlocks; i++) {
CGFloat totalWidth = BLOCK_WIDTH * i;
CGRect frame = CGRectMake(0, 0, BLOCK_WIDTH, BLOCK_HEIGHT);
frame.origin.x = totalWidth;
BlockView *view = [[BlockView alloc] initWithImage:[UIImage imageNamed:#"block.png"]];
OR!!!
BlockView *view = [[BlockView alloc] initWithFrame:frame];
view.frame = frame;
NSString *number = [NSString stringWithFormat:#"%ld", (long)i + 1];
view.numberLabel.text = number;
[self addSubview:view];
[_blocks addObject:view];
}
}
Googling gave me the impression that this is very common problem, but I haven't found any solution how to beat this. Moreover, I still do not understand, why numbers are in totally right order, the only problem is view position.
The problem is that you're setting the frame of the red view to the superview's frame instead of its bounds. Since the red view is a subview of the BlockView, its frame needs to be relative to its superview, which you get with bounds (its not clear why you even need the red view, as opposed to setting the background color of the block view to red).
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.redView = [self redViewWithFrame:self.bounds];
[self addSubview:self.redView];
//label-related stuff
}
return self;
}
I think you should set the frame origin to (0,0) in redViewWithFrame in ordre to hâve superposed views
I tried, in IOS 7 to subclass an UISearchBar so that the place holder is always left aligned.
I did this:
- (void)layoutSubviews {
[super layoutSubviews];
UITextField * tv = self.textField;
tv.textAlignment = NSTextAlignmentLeft ;
}
If tv.textAlignment = NSTextAlignmentRight, then I manage to make the text to the right.
However, the text when UISearchBar is empty, and display the placeholder, always shows to the center.
I wonder why. I put it in layoutSubviews method. Hence, it should be drawn every time the control is drawn.
Can you try this using UIAppearance.
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextAlignment:NSTextAlignmentLeft];
Downvoters, please read this : note from Apple:
Note: iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.
I have same issue before some days ago, but there is no way to change the alignment of search bar's placeholder text, I tested with so many "SO" answer but no one was working.
At last I decided with following Fixing.
Add some white space in Left/right (as you want) of placeholder text
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.1) {
// Load resources for iOS 6.1 or earlier
self.searchBar.placeholder = #"hello";
}
else
{
// Load resources for iOS 7 or later
// Add some white space in Left/right *(as you want)* here i added to left
self.searchBar.placeholder = #" hello";
}
Through NSLog the searchBar's textField subviews, can get such a result:
<_UISearchBarSearchFieldBackgroundView: 0x8d492e0; frame = (0 0; 304 28); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x8d49410>> - (null),
<UIImageView: 0x8d488a0; frame = (102.5 7.5; 12.5 12.5); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8d48f80>> - (null),
<UISearchBarTextFieldLabel: 0x8d4b410; frame = (122.5 1; 181.5 25); text = 'hello000000'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8d4b520>>
)
you can see, UISearchBarTextFieldLabel is the UI to show placeholder, here is 'hello000000', which is a subclass of UILabel. Therefore, set searchBar's textField textAlignment will not affect placeholder position directly. And the layout of placeholder label is handled by searchBar's textField - (void)layoutSubviews , whereas you can't override inner textField this method.
A custom searchBar base on UIView, add your subclass of UITextField which override - (void)layoutSubviews, Or even add add UILabel where textfield's text is empty, remove while not. maybe a solution.
Add some my test code:
#interface CustomSearchBar : UIView {
}
#end
#implementation SharedSearchBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_textField = [[SearchTextField alloc] initWithFrame:CGRectMake(10, 6, frame.size.width-20, frame.size.height-12)];
_textField.leftView = UIImageViewNamed(#"search_glass");
[self addSubview:_textField];
}
return self;
}
#end
#interface SearchTextField : UITextField
#end
#implementation SearchTextField
- (void)layoutSubviews{
[super layoutSubviews];
for (UIView *subView in self.subviews) {
if ([subView isKindOfClass:[UILabel class]]) {
UILabel *lb = (UILabel *)subView;
lb.textAlignment = UITextAlignmentRight;
lb.text = #"123123";
CGRect frame = lb.frame;
frame.origin.x = textFiled.frame.size.width-frame.size.width;
lb.frame = frame;
break;
}
}
}
#end
first take a look on this picture from localScope app :
i have 2 (simple?) questions :
how can i paginate my icons like this?
how can i detect witch icon is " selected "
thank you.
Answer to the first question: You have to make your scroll view as big as the page size, enable its pagingEnabled property, then somehow make it to display elements and respond to touches outside of its bounds. See this code and these links:
#interface SmallPagedScrollView: UIScrollView <UIScrollViewDelegate> {
UIEdgeInsets responseInsets;
NSMutableArray *items;
}
#implementation SmallPagedScrollView
#synthesize responseInsets;
- (id)init
{
if ((self = [super initWithFrame:CGRectMake(x, y, w, h)]))
{
self.backgroundColor = [UIColor clearColor];
self.pagingEnabled = YES;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.clipsToBounds = NO;
CGFloat hInset = 3 * self.width / 2;
self.responseInsets = UIEdgeInsetsMake(0.0f, hInset, 0.0f, hInset);
self.delegate = self;
items = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc
{
[items release];
[super dealloc];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint parentLocation = [self convertPoint:point toView:self.superview];
CGRect responseRect = self.frame;
responseRect.origin.x -= self.responseInsets.left;
responseRect.origin.y -= self.responseInsets.top;
responseRect.size.width += self.responseInsets.left + self.responseInsets.right;
responseRect.size.height += self.responseInsets.top + self.responseInsets.bottom;
return CGRectContainsPoint(responseRect, parentLocation);
}
See also Paging UIScrollView in increments smaller than frame size (Split's answer)
Answer to the second question: you can calculate the selected page using this formula:
int selectedIndex = (scrollView.contentOffset + scrollView.size.width / 2) / scrollView.size.width;
Well one clean & memory efficient approach is to have a UINavigationController & UIToolBar like so -
When the user taps on any button in the UIToolBar invoke that particular viewController by popping and pushing them.
I hope its clear that the look and feel can be achieved close to what you are showing in the image, I am talking about the functionality.
We have a window filled with little view squares (think of a Calculator).
For a specific view on the window we want display a single string in the view without using the Interface Builder to add the string.
We need to be able to change the string and have the view refresh.
How do we programmatically add a string to a view and show it?
Update:
Ok here is the code we have currently. Nothing special in the header file.
I suppose the real quandry is considering we can easily get the background color to change, why is it that our text is just not showing??
Both versions are in there, would be happy to get 'apples' or 'oranges' displaying.
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
bgString = #"orange";
UILabel* aLabel = [[UILabel alloc] init];
aLabel.text = #"apple";
self.textLabel = aLabel;
[aLabel release];
[self addSubview:textLabel];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
[[UIColor yellowColor] setFill];
UIRectFill(rect);
[self drawStringCenteredIn:rect];
}
- (void)drawStringCenteredIn:(CGRect)r {
//CGSize strSize = [bgString size];
CGPoint strOrigin;
strOrigin.x = r.origin.x; //+ (r.size.width - 10)/2;
strOrigin.y = r.origin.y; //+ (r.size.height - 10)/2;
//[bgString drawAtPoint:strOrigin withFont:[UIFont fontWithName:#"Helvetica" size:10]];
[textLabel drawTextInRect:r];
}
In your view controller's .h:
#interface MyViewController
{
UILabel* label;
}
#property (nonatomic, retain) UILabel* label;
In your view controller's .m:
- (void)dealloc
{
[label release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel* aLabel = [[UILabel alloc] init];
aLabel.text = #"Initial Text";
self.label = aLabel;
[aLabel release];
[self.view addSubview:aLabel];
}
- (void)viewDidUnload
{
[self.label removeFromSuperview];
self.label = nil;
}
// Call this when you need to update the label
- (void)updateLabel
{
self.label.text = #"Some updated text";
}
Did that from memory but it should work.
Try this:
UILabel* aLabel = [[UILabel alloc] initWithFrame:[self bounds]];
If you are creating the label manually, you need to set it's frame manually too.
Frame itself is size and position inside parent view(superview).
In my example i've set the frame of label to occupy the entire view. If you need your custom size you can use:
UILabel* aLabel = [[UILabel alloc] initWithFrame:CGRectMake(x,y,width,height)];
Where (x,y) - position of the top left corner of your label.
How about creating a UILabel and adding it to the view?
If you subclass the UIView, you can draw your string in the view's drawRect. This allows great flexibility in modifying the text, its appearance, and its placement (you can even animate it around, spin, rotate, etc.)
Call setNeedsDisplay on the view after you change your NSString. Then do an drawAtPoint:withFont: on the NSString when the drawRect is called.