I have a LoginViewController with two text fields and a button. I used storyboard, so these are not created programmatically. They are below each other.
Here's the .h file
#interface LoginViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *pw1;
#property (weak, nonatomic) IBOutlet UITextField *pw2;
#property (weak, nonatomic) IBOutlet UIButton *loginBtn;
- (IBAction)loginBtnPressed:(id)sender;
- (IBAction)singleTapRecognized:(id)sender;
#end
The pw2 is shown only on the first run, when the user creates a new password and confirms it in pw2. Otherwise it is hidden. In that case I move the button up to put the button closer to pw1.
CGSize size = self.pw2.frame.size;
CGRect rect = self.loginBtn.frame;
CGRect newFrame = CGRectMake(rect.origin.x, rect.origin.y-size.height, rect.size.width,rect.size.height);
[self.loginBtn setFrame:newFrame];
So far, so good. Up to here all works as expected. But now.....
when I leave the pw1 blank, the program checks the field, detects it is empty, hides the keyboard and shows a UIAlertView. This works fine.
when I put in some characters that are not correct, the program does almost the same, but moves the button down to its original position.
same behavior when some characters entered and tapped anywhere in the view
I found out, that this only happens, when I am hiding the keyboard. Otherwise the button stays at its "upped" position as expected.
Here is my keyboard hide:
-(void) hideKeyboard {
if (self.pw1.isFirstResponder)
[self.pw1 resignFirstResponder];
else if (self.pw2.isFirstResponder)
[self.pw2 resignFirstResponder];
}
Any ideas what happens?
Sorry about my english. English is not my native language.
I want to share my solution with you. I am sure there is a more elegant way to code, but this one works fine more me.
I added property in the .h file to hold the new constraint for the button.
#property NSLayoutConstraint *loginButtonVerticalSpace;
Then i add a routine to find the "old" constraint as defined in the storyboard.
-(NSLayoutConstraint *) findButtonConstraintForItem:(id) item
secondItem:(id) secondItem //may be nil
firstAttribute:(NSLayoutAttribute)firstAttribute
secondAttribute:(NSLayoutAttribute)secondAttribute
{
NSArray *cons = self.view.constraints;
for (NSLayoutConstraint *ns in cons) {
if (ns.firstItem==item)
{
if (ns.firstAttribute==firstAttribute)
{
if (secondItem==nil)
return ns;
if (ns.secondItem==secondItem && ns.secondAttribute==secondAttribute)
return ns;
}
}
}
return nil;
}
And at least i changed my "moveButton" method to the following:
- (void) moveButtonUp{
NSLayoutConstraint *consOld = [self findButtonConstraintForItem:self.loginBtn
secondItem:self.pw1
firstAttribute:NSLayoutAttributeTop
secondAttribute:NSLayoutAttributeBottom];
if (!consOld) // should not happen
return;
if (self.loginButtonVerticalSpace) // work already done
return;
CGSize size = self.pw2.frame.size;
self.loginButtonVerticalSpace =[NSLayoutConstraint
constraintWithItem:self.loginBtn
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.pw1
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:consOld.constant-size.height];
self.loginButtonVerticalSpace.priority=1000;
[self.view addConstraint:self.loginButtonVerticalSpace];
[self.view removeConstraint:consOld];
}
Thank you so much, guys....
Related
I want to change my calendar height's constraint 124 to zero when calendar button is clicked how can I do that? I want to use button statement but I couldn't at objective c. Thanks.
#property (weak, nonatomic) IBOutlet UIBarButtonItem *calendarButton;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *heightOfCalendar;`
- (IBAction)calendarAction:(id)sender {
}
you need to set constant property
heightOfCalendar.constant = requiredheight
You can Do like this to change size on each tap
- (IBAction)calendarAction:(id)sender {
sender.selected = !sender.selected;
heightOfCalendar.constant = (sender.selected)?0:124;
}
Doing it with an animation will looks good
(IBAction)calendarAction:(id)sender {
[UIView animateWithDuration:0.5
animations:^{
heightOfCalendar.constant =0;
[self.view layoutIfNeeded];
}];
}
Write this code in your IBAction:
- (IBAction)calendarAction:(id)sender {
self.heightOfCalendar.constant = 0;
}
Also make sure you have your IBAction connection set properly.
And I see a ` symbol at the end of the following statement:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *heightOfCalendar;`
I create a demo for checking UITextView scrollEnabled.
It only contains 1 UITextView and 2 button enable and disable scroll
I test on simulator and device iOS 8, if I click the disable scroll button, the UITextView will disable scroll correctly, but after that I
click on enable scroll, the UITextView won't enable scroll
However, I test on device iOS9, the enable and disable scroll work well.
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextView *myTextView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)enableScrollButtonPressed:(id)sender{
self.myTextView.scrollEnabled = YES;
}
- (IBAction)disableScrollButtonPressed:(id)sender{
self.myTextView.scrollEnabled = NO;
}
#end
Any idea to fix this problem? If someone don't understand my explain please check
my demo project
Any help or suggestion would be great appreciated
The problem is that in iOS 8, contentSize is not adjusted correctly when scrollEnabled is changed. A small adjustment to your enableScrollButtonPressed method will successfully work around the problem.
-(IBAction)enableScrollButtonPressed:(id)sender
{
self.myTextView.scrollEnabled = YES;
self.myTextView.contentSize = [self.myTextView sizeThatFits:self.myTextView.frame.size];
}
Yeah you are right disabling and enabling scroll of textview is not working in iOS8 it may be a bug or anything else, let it be.
We can disable or enable scroll of text view by just changing the ContentSize of textview .
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextView *myTextView;
#end
#implementation ViewController
-(IBAction)enableScrollButtonPressed:(id)sender{
CGSize scrollableSize = CGSizeMake(self.myTextView.bounds.size.width+20, self.myTextView.bounds.size.height+20);
[self.myTextView setContentSize:scrollableSize];
}
-(IBAction)disableScrollButtonPressed:(id)sender{
[self.myTextView setContentOffset:CGPointMake(0, 0)];
[self performSelector:#selector(StopScroll) withObject:nil afterDelay:0.1];
}
-(void)StopScroll{
CGSize scrollableSize = CGSizeMake(self.myTextView.bounds.size.width, self.myTextView.bounds.size.height/2);
[self.myTextView setContentSize:scrollableSize];
}
#end
I had tested the above code it is working fine for me i'am using xcode 7.2.1 and iOS 8.3. Give it a try and let me know the result
After searching hours i am found one way
-(IBAction)enableScrollButtonPressed:(id)sender{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.myTextView.scrollEnabled = YES;
[self.myTextView setText:self.myTextView.text];
self.myTextView.layoutManager.allowsNonContiguousLayout = false;
});
}
-(IBAction)disableScrollButtonPressed:(id)sender{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.myTextView.scrollEnabled = NO;
});
}
condition for that is you need to scroll first then you it will work perfectly
Means when disable scroll button is tapped textview should be scrolled at some position must not to at default position
Please follow below steps simply
self.myTextView.scrollEnabled = NO;
self.myTextView.userInteractionEnabled = YES;
self.myTextView.scrollEnabled = YES;
you need to layout your views after enabling or disabling scroll view in your view.
use viewWillLayoutSubviews/layoutIfNeeded methods.
change your property as follows.
in your code..
#property (weak, nonatomic) IBOutlet UITextView *myTextView;
change property attribute as follows:
#property (strong, nonatomic) IBOutlet UITextView *myTextView;
then it'll work fine. i checked it.
I tried my code and it worked properly
IBOutlet UITextView *tv;
//---------Enable button action.
-(IBAction)enable:(id)sender
{
tv.scrollEnabled=YES;
}
//---------Disable button action.
-(IBAction)disable:(id)sender
{
tv.scrollEnabled=NO;
}
try your code once more in device.
Try this one
func scrollViewDidScroll(_ scrollView: UIScrollView) {
tableView.setContentOffset(CGPoint.zero, animated: false)
}
I know there are some questions about how to show a toolbar above keyboard.
I read some of them. But, I could not make the toolbar which have TextView like message app. I want to make a toolbar which is showed when button tapped.
I also use IB to create a toolbar because I need some adjustment to appearance.
The code now I make is below.
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIToolbar *ibToolbar;
// this text view is subview of ibToolbar
#property (weak, nonatomic) IBOutlet UITextView *barTextView;
#end
- (void)viewDidLoad {
[super viewDidLoad];
[self.ibToolbar removeFromSuperview];
self.barTextView.inputAccessoryView = self.ibToolbar;
}
- (IBAction)buttonPushed:(id)sender {
[self.barTextView becomeFirstResponder];
}
When I tap the button, I want to look at toolbar above keyboard to put text on textView.
But, nothing happened when I push the button.
I think that why this code does not work, also because the viewController's view does not parent view of the toolbar,so [self.barTextView becomeFirstResponder] can not be effective.
I think that something else which I did is needed to work the things.
If anyone know do the things work, please tell me.
I am struggling with an issue which I simply do not understand. Here is the following scenario:
I try to create a multi-view wizard in iOS. After much consideration I decided to go with an approach that may not be ideal but will help me keep the code clean. I don't want to chain ten view controllers and manage the manually.
The wizard has one view controller. At start it loads a certain amount of UIView's into the view and allows me to scroll left and right. Code below.
The UIViews are created in interface builder and I have one XIB file containing all the views.
Each UIView has more IBOutlets (for example a label) which are connected to the File Owner, in this case the wizard view controller.
This all works really well EXCEPT for the fact that the labels on the loaded UIViews don't update. Here is the problem:
I call the wizard and it loads all UIViews as well as Outlets right then
I skip through the wizard.
But then the problem occurs:
Example:
Step 2 has a button to call the address book (which works)
The Address book opens, returns an object (which works)
But my Label on UIView "Step 2" doesn't update when I assign a value.
Only after I remove this UIView from the Wizard and add it back, it works. It seems to me that the UIView loads correctly but any subsequent changes to their outlets don't work.
By the way, I verified that the label as well as the buttons and everything is connected probably. I just cannot update the label when its loaded as part of the XIB and the value changes after the initial load.
Code:
Wizard loading UIViews:
-(void)configureWizard
{
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.scrollEnabled = NO;
self.contentSize = CGSizeMake(CGRectGetWidth(self.frame) * [wizardDelegate numberOfSteps], CGRectGetHeight(self.frame));
self.delegate = self;
}
-(void)loadWizard
{
[self configureWizard];
[self updateNavButtonsIfNecessary];
currentIndex = 0;
for (int i = 0; i<[wizardDelegate numberOfSteps]; i++)
{
[self loadWizardStepAtIndex:i];
}
}
- (void)loadWizardStepAtIndex:(int)index {
CGRect frame = self.frame;
frame.origin.x = CGRectGetWidth(frame) * index;
frame.origin.y = 0;
UIView *view = [wizardDelegate viewForStepAtIndex:index];
view.frame = frame;
[self addSubview:view];
}
Wizard Main View Controller.h (File Owner)
#property (strong, nonatomic) IBOutlet UIView *memberwizard_1;
#property (strong, nonatomic) IBOutlet UIView *memberwizard_2;
#property (strong, nonatomic) IBOutlet UIView *memberwizard_3;
#property (strong, nonatomic) IBOutlet UIView *memberwizard_4;
#property (strong, nonatomic) IBOutlet UIView *memberwizard_5;
#property (strong, nonatomic) IBOutlet UIView *memberwizard_6;
#property (strong, nonatomic) IBOutlet UIView *memberwizard_7;
#property (strong, nonatomic) IBOutlet UIButton *wizard2_lookupExistingContact;
#property (strong, nonatomic) IBOutlet UILabel *wizard2_nameLabel;
Wizard Main View Controller.m (File Owner)
-(UIView *)viewForStepAtIndex:(int)index
{
[[[NSBundle mainBundle]loadNibNamed:#"wizard_newMember" owner:self options:nil] lastObject];;
switch (index) {
case 0:
return self.memberwizard_1;
break;
case 1:
return self.memberwizard_2;
break;
case 2:
return self.memberwizard_3;
break;
case 3:
return self.memberwizard_4;
break;
case 4:
return self.memberwizard_5;
break;
case 5:
return self.memberwizard_6;
break;
case 6:
return self.memberwizard_7;
break;
default:
return self.memberwizard_1;
}
}
Return function of the address book
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
{
[self displayPersonRecord:person];
[self dismissViewControllerAnimated:YES completion:nil];
}
Trying to update the label on the second UIView (this is where the issue occurs - it doesnt work).
-(void) displayPersonRecord:(ABRecordRef)person
{
NSString *firstName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonLastNameProperty);
NSLog(#"Name is %#", [NSString stringWithFormat:#"%# %#", firstName, lastName]);
self.wizard2_nameLabel.text = [NSString stringWithFormat:#"%# %#", firstName, lastName];
[self.wizardDelegate refresh:self.memberwizard_2]; //if called, the current view will be removed and re-added - then the label gets updated.
}
But as I mentioned if I reload the view then it works (see screenshot)
Reload method IMPORTANT: this is nasty code and not used in production, its just a test to see if a reload of the view helps.
-(void)refresh:(UIView *) oldView
{
[oldView removeFromSuperview];
CGRect frame = self.frame;
frame.origin.x = CGRectGetWidth(frame) * currentIndex;
frame.origin.y = 0;
//UIView *view = [wizardDelegate viewForStepAtIndex:currentIndex];
oldView.frame = frame;
[self addSubview:oldView];
}
If at this point I remove self.memberwizard_2 view from the wizard and add it back in everything works but it doesn't right away. Could that be related to way I initiate the UIViews? By the way the NSLog in the last paragraph works as well showing the name of the contact.
In case anyone knows an easier way to deal with wizards let me know :) PageViewControllers are painful because I need to hand-over one object from screen to screen but need to display different actions in each screen.
Thanks much and apologies for the code mess, I tried to make it as clear as possible but maybe it isn't.
Here's where I think it goes wrong. For each "WizardStep", you call viewForStepAtIndex which loads the NIB and returns the relevant view. You then add that as a subview. But when you process the next WizardStep, the NIB gets loaded again, which creates new instances of all its objects and reconnects all the Outlets and Actions to the newly loaded objects. So the view you have just added as a subview (and so is displayed) is no longer pointed to by your outlets. Thus when you update the label, it is not updating the label on screen, but a completely different label on a view that's not in the hierarchy at all.
So, I would revise your code so that the loadNibNamed: method gets run only once.
I have a UIView without any associated UIViewController constantly animating with auto layout and layoutIfNeeded (see code bellow).
But when this view (which is contained in another view) disappears (for example when a modal view covers the view it is contained in) and after I dismiss this modal view, the animating view isn't animating anymore.
I managed to animate it back with the didMoveToWindow:animated method but i'm not sure this is the proper approach.
#interface AnimatingView()
#property (strong, nonatomic) UIView *aSubview;
#property (strong, nonatomic) NSLayoutConstraint *constraintTop;
#property (assign, nonatomic, getter = isStarted) BOOL started;
#property (assign, nonatomic) BOOL stopAnimation;
#end
#implementation AnimatingView
- (id)init
{
self = [super init];
if (self) {
self.stopAnimation = YES;
/* setting base auto layout constraints */
}
return self;
}
-(void)animate{
float aNewConstant = arc4random_uniform(self.frame.size.height);
[UIView animateWithDuration:ANIMATION_DURATION animations:^{
[self removeConstraint:self.constraintTop];
self.constraintTop = [NSLayoutConstraint constraintWithItem:self.aSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:aNewConstant];
[self addConstraint:self.constraintTop];
[self layoutIfNeeded];
} completion:^(BOOL finished) {
if (finished && !self.stopAnimation) {
[self animate];
}
}];
}
- (void)didMoveToWindow{
[super didMoveToWindow];
if ([self isStarted]) {
[self stop];
[self start];
}
}
-(void)start{
if (![self isStarted]) {
self.stopAnimation = NO;
[self setStarted:YES];
[self animate];
}
}
-(void)stop{
if ([self isStarted]) {
self.stopAnimation = YES;
[self setStarted:NO];
}
}
#end
You can always use a behavioral pattern : KVO to control the state of the UIView or what I would do, use a notification to animate the view when you want to. Both options are valid. You can have as well a delegate associated with this class and implement the behavior you want in your UIView.
Assuming your view is contained in another view which is contained in a Controller, you can set a BOOL variable called didReturnFromModalChildView which you set it true every time you present that modal view. When you dismiss that modal view, your application will call -(void)viewWillAppear:(BOOL)animated. Here you have to check if that BOOL variable is set to Yes and then use a public method calling if you have a reference to the view you want to animate, or a delegation process or observer-notification pattern. Don't forget to set didReturnFromModalChildView to false.
I hope this will help you.