put UILabel/UITextField on next line instead of pushing out of view - objective-c

I'm creating a natural language form and whenever the user enters an input which is quite large, I want the entire line to move to the next line (just like an UITextView). Right now, I get this result:
This indicates two obvious problems: for one: the element the UITextField is pushing should go to the next line, and secondly, when spacing back, the element that was 'pushed away' does not get 'pushed back' into place. Also, the UITextField should move to the next line when exiting the view.bounds. It is arbitrary whether it's best to use a UITextField or UITextView for. It should be applicable to a situation in picture 2.
This is a more graphical approach to what I'm trying to achieve:
How do I solve this? And is this the right approach?
Update
The answer of Robert is very good one, next to some bugs that it still has there are is also the issue that it's not flexible. I've started refactoring the code and tried to subclass a UITextField and a UITextView, following the approach of Robert.
When subclassing the code there needs to be some delegation by the UITextField to the UITextView. Secondly, every part of the sentence needs to be split whenever there's a UITextField in between, but I feel like that can be hard coded into the VC as well. The constraints need to be converted to code as well.
Whenever I've got a solution to either one of all those problems I'll update the question and hopefully get to a flexible solution :)

Your approach works for me.
Let's say you have a UITextView that displays the selectable, but non-editable full sentence, including the entered parameter values. And then you have an editable UITextField for each form parameter. With this setup you can leave it to the UITextView to handle the text flow and use the UITextViews to handle the input.
In order to let the UITextField appear within the text flow, the trick is to hide it – or rather reduce it to the width of its caret – and display it at the position of last character of the parameter's value's.
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UITextView *fullTextView;
#property (strong, nonatomic) IBOutlet UITextField *friendField;
// Using AutoLayout constraints to position the friendField
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *friendFieldLeadingConstraint;
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *friendFieldTopConstraint;
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *friendFieldWidthConstraint;
#property (assign, nonatomic) CGFloat initialFriendFieldWidth;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Store the intrinsic size of the friendField displaying the placeholder
// (there's probably a better way to this than storing the value on view load)
self.initialFriendFieldWidth = self.friendField.intrinsicContentSize.width;
}
- (IBAction)friendFieldEditingChanged:(UITextField *)friendField {
// Insert the friend name into the sentence
NSString *sentencePart1 = #"I'm paying ";
NSString *sentencePart2 = #"\n$ amount\nfor description";
self.fullTextView.text = [#[sentencePart1, friendField.text, sentencePart2] componentsJoinedByString:#""];
// Render the fullTextView, so that we can retrieve the friend name's last character position
[self.fullTextView setNeedsLayout];
[self.fullTextView layoutIfNeeded];
// Retrieve the frame of the friend name's last character (in relation to the textView's coordinates)
UITextPosition *last = [self.fullTextView positionFromPosition:self.fullTextView.beginningOfDocument offset:sentencePart1.length + friendField.text.length];
UITextPosition *secondToLast = [self.fullTextView positionFromPosition:last offset:-1];
UITextRange *range = [self.fullTextView textRangeFromPosition:secondToLast toPosition:last];
CGRect rangeRect = [self.fullTextView firstRectForRange:range];
// Here comes the trick:
// The friendField's width will be reduced to the width of the caret and
// placed at the last character's position within the fullTextView.
// This way the UITextView handles the display of the full text,
// incl. the parameter values. And the UITextFields will handle the input,
// while only appearing as a caret.
// Retrieve the caret width
CGFloat width = [self.friendField caretRectForPosition:nil].size.width;
// If no text is entered, unfold friendField to reveal the placeholder
if (friendField.text.length == 0) {
width = self.initialFriendFieldWidth;
}
// Using AutoLayout constraints (see Main.storyboard)
// Note: A proper implementation needs to display the caret right where it is in
// relation to the input value. For now we only display it at the end of the value.
self.friendFieldLeadingConstraint.constant = - rangeRect.origin.x - rangeRect.size.width;
self.friendFieldTopConstraint.constant = - rangeRect.origin.y;
self.friendFieldWidthConstraint.constant = width;
}
It will look like this: (highlighting the textfield dimensions)
You can download a fully working example here: https://github.com/widescape/NaturalLanguageForm

Related

Populating a UILabel from a NSString

wow weird !!, I am passing a value NSString from my prepare for segue to another class. Ok, the string has a value and when passed to my -(void) the value is still there. I have an IBOutlet UILabel connected but the below code does not populate the UILabel. I am logging this from the void method.
2016-09-01 14:21:56.839 ApiAIDemo[1263:586780] The msgString has this in it {
speech = "The Moon is Earth's only permanent natural satellite. It is the fifth largest natural satellite in the Solar System, and the largest among planetary satellites relative to the size of the planet that it orbits. It is the second-densest satellite among those whose densities are known.";
}
Here is my property.
#property (weak, nonatomic) IBOutlet UILabel *myMsgLabel;
-(void)passedMsgFromVoicButtonVC:(NSString *)msgString{
**NSLog(#"The msgString has this in it %#",msgString);**
self.myMsgLabel.text = [NSString stringWithFormat:#"%#",msgString];
NSLog(#"BELOW the msgLabel has this in it %#",_myMsgLabel.text);
}
Create a non-outlet property in your controller to hold the string that's being passed in from the segue. Then update the outlet in viewDidLoad.
The problem is that segues are called before iOS has set up the controller's UI correctly.

How I can get the value of my UITextField?

how I can get the value of my UITextField ? When I declare my UITextField in the Storyboard, I know but like this, I don't know.
(sorry for my English, I'm French)
Thank you in advance for your answer.
//Ajout d'un Text Field
CGRect rectTF = CGRectMake(10,70,100,20); // Définition d'un rectangle
UITextField *articleSaisi = [[UITextField alloc] initWithFrame:rectTF];
articleSaisi.borderStyle = UITextBorderStyleLine;
articleSaisi.placeholder = #"Article";
[self.view addSubview: articleSaisi];
I am not quite sure whether you want to access the value or you want to associate your UI element to your code:
Try this if you are saying that you want to access the text value.
atricleSaisi.text
Try to control drag the UI element to either your corresponding .h or .m file so that it can create IBOutlet for you if you are saying that you want to connect your UI element to your code.
Highly recommend you go check the documentation.
if you want to know when the user pressed return, as per your comment, then you should create a UITextViewDelegate for your text view and define its textFieldShouldReturn method:
Discussion
The text field calls this method whenever the user taps the return button. You can use this method to implement any custom behavior when the button is tapped.
Also, give a look at textFieldDidEndEditing, which is called whenever there is a focus change and you should define to correctly handle user input.
Old answer:
If I do not understand you incorrectly, you want to create a UITextField programmatically (i.e., not through a Storyboard).
In this case, you should put you initialisation code inside the viewDidLoad method of your view controller and make sure that you define a property for the text field instead of a local variable:
#interface MyViewController : UIViewController
....
#property (nonatomic, strong) UITextField* articleSaisi;
...
#end
- (void)viewDidLoad {
CGRect rectTF = CGRectMake(10,70,100,20); // Définition d'un rectangle
self.articleSaisi = [[UITextField alloc] initWithFrame:rectTF];
self.articleSaisi.borderStyle = UITextBorderStyleLine;
self.articleSaisi.placeholder = #"Article";
[self.view addSubview:self.articleSaisi];
}
If you do so, you can access the text field value from any other method in the view controller through its text property:
self.articleSaisi.text

Changing image in different view

I am attempting to change the character image in the game that I am creating but I am having some issues. I have a view controller where I will have all of the characters as their own buttons, and by selecting a button it should change the character image in the GameViewController
change.h
- (IBAction)charOne:(id)sender;
change.m
- (IBAction)charOne:(id)sender
{
GameViewController *changeView = [GameViewController game];
UIImageView *Romo = [GameView changeView];
[ChangeView setImage:[UIImage imageNamed:#"testOne.png"]];
NSLog(#"Test");
}
GameViewController.m
{
IBOutlet UIImageView *changeView;
}
#property (strong, nonatomic) IBOutlet UIImageView *changeView;
This code isn't working for me, I do not receive an error message but nothing is changed after the image is selected.
On the GameViewController the UIImage name that I am attempting to change the image for is named Romo.
Any help will be appreciated
try :
[Romo setImage:[UIImage imageNamed:#"testOne.png"]];
instead of :
[ChangeView setImage:[UIImage imageNamed:#"testOne.png"]];
You're calling the setImage method on a class rather than an instance of a class. Since Romo is the instance of UIImageView you want to affect (which is a reference pointer to the UIImageView on your game view controller), that should be the instance you affect.
Also, bad practice to name instances starting with a capital letter -- they turn out looking like classes.

Sending multiple UITextField fields to a UITextView on a second ViewController (using a segue)

Firstly, Im new to Objective-C and i'm currently following several tutorials. Apologies if this is an obvious solution. I did use the search for several hours and couldn't find a solution.
I have no problem understanding how to send a data from a single UITextField to a UITextView in a new view controller.
MY QUESTION IS: I want to populate the UITextView on the second view controller with all the users information they entered into the UITextField when they press the button.
IMAGE OF LAYOUT
The code I used to populate the UITextView with 'Name' is:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier]isEqualToString:#"mySegue"]) //will return true so will start segue
{
ViewControllerTwo *ViewControllerTwo = [segue destinationViewController];
ViewControllerTwo.Name = sendName.text;
}
}
I'm sure to send the rest of the form over is simple but right now I just can't think of a solution.
Create more properties for the information in ViewControllerTwo.h. For example if you wanted to add the users age.
#property (nonatomic, strong) int age;
Set the value of the age in your prepareForSegue Method from the text the user enters into the field.
ViewControllerTwo.age = [ageTextField.text intValue];
In ViewControllerTwo.m build a string with all these properties and set the UITextView text equal to entire string.
NSString *userInformation = [[NSString stringWithFormat:#"%# \n %d", name, age];
Set your UITextView.text property equal to userInformation

Referencing an array of IBOutlet 's (NSButton)

I am making an application where there are 32 Check Box, and a NSTextField.
If the user clicks on the NSTextField the buttons shall assume the value that describes the binary rappresentation of this number.
No problem receiving the "clicked" action on the NSTextField, but for the buttons I have declared an array of 32 NSButtons:
#import <Foundation/Foundation.h>
#interface Handler : NSObject
{
#private
IBOutlet NSTextField* textField;
IBOutlet NSButton* bits[32]; // here are the buttons
}
- (void)awakeFromNib;
- (void) setTextField : (int) value;
- (int) getTextField;
#end
But when I try to link a Check Box with the IBOutlet "bits", I can't do it for each member.
So I can only make that array of 32 pointers to NSButton to one box.
I also show an image:
This is a problem for me, do I have to manually declare 32 different outlets with 32 different names?
You do not have to use 32 different IBOutlet references. You can declare an IBOutletCollection:
#property (retain, nonatomic) IBOutletCollection(NSButton) NSArray *buttons;
Use this to link them all up. Keep in mind that the order of the buttons is non-deterministic at runtime, meaning, you cannot guarantee that the buttons will be in any specific order when the app is running.
This is probably a good example of using an NSMatrix object.
You can add one button to your interface and then with the button selected in Xcode 4 go to Editor > Embed In > Matrix. Then you can option drag on a corner of the button to expand it into a matrix.
NSMatrix allows you to retrieve the cell values by searching for a given tag or by column/row coordinates.
HOW TO:
1) Embedding the NSButton object:
2) Option-Drag any of the button corners to expand the matrix:
I expanded it into a matrix.But graphically it looks like a single button.
This is what I get: