Невозможно идентифицировать компьютеры, обращающиеся к веб-сайту, без участия их владельцев. Однако, если вы позволите, вы можете сохранить файл cookie, чтобы идентифицировать машину, когда он снова посещает ваш сайт. Ключ в том, что посетитель находится под контролем; они могут удалить cookie и появиться в качестве нового посетителя в любое время.
Вам понадобится ScrollView
, только если содержимое, которое у вас сейчас, не умещается на экране iPhone.
(Если вы добавляете ScrollView
в качестве супервизора компонентов. Просто чтобы заставить TextField
прокручиваться вверх при появлении клавиатуры, это не нужно.)
Для отображения текстовые поля
, не скрытые клавиатурой, стандартным способом является перемещение вверх / вниз по виду с текстовыми полями всякий раз, когда отображается клавиатура.
Вот пример кода:
#define kOFFSET_FOR_KEYBOARD 80.0
-(void)keyboardWillShow {
// Animate the current view out of the way
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)keyboardWillHide {
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if ([sender isEqual:mailTf])
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
}
}
//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // if you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
}
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
Одна вещь, которую следует принять во внимание, - хотите ли вы когда-нибудь использовать UITextField
отдельно. Я не встречал ни одного хорошо разработанного приложения для iPhone, которое действительно использует UITextFields
за пределами UITableViewCells
.
Это будет дополнительная работа, но я рекомендую вам реализовать ввод всех данных просматривает таблицы просмотров. Добавьте UITextView
в свой UITableViewCells
.
Я не уверен, что перемещение просмотра - это правильный подход, я сделал это другим способом, размер UIScrollView. Я объяснил это в деталях на небольшом статьи
Код RPDP успешно перемещает текстовое поле в сторону от клавиатуры. Но когда вы прокручиваете вверх после использования и отключения клавиатуры, верхняя часть прокручивается вверх из поля зрения. Это верно как для симулятора, так и для устройства. Чтобы прочитать содержимое в верхней части этого представления, необходимо перезагрузить представление.
Разве его следующий код не должен вернуть представление?
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
Небольшое исправление, которое работает для многих UITextFields
#pragma mark UIKeyboard handling
#define kMin 150
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if (currTextField) {
[currTextField release];
}
currTextField = [sender retain];
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
[self setViewMovedUp:YES];
}
}
//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // if you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y = kMin - currTextField.frame.origin.y ;
}
else
{
// revert back to the normal state.
rect.origin.y = 0;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)keyboardWillShow:(NSNotification *)notif
{
//keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
{
[self setViewMovedUp:YES];
}
else if (![currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y < kMin)
{
[self setViewMovedUp:NO];
}
}
- (void)keyboardWillHide:(NSNotification *)notif
{
//keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
if (self.view.frame.origin.y < 0 ) {
[self setViewMovedUp:NO];
}
}
- (void)viewWillAppear:(BOOL)animated
{
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:self.view.window];
}
- (void)viewWillDisappear:(BOOL)animated
{
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}
Чтобы вернуться в исходное состояние просмотра, добавьте:
-(void)textFieldDidEndEditing:(UITextField *)sender
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
Вот хакерское решение, которое я придумал для конкретного макета. Это решение похоже на решение Мэтта Галлахера в том, что оно прокручивает секцию в поле зрения. Я все еще новичок в разработке iPhone и не знаком с принципами работы макетов. Итак, этот хак.
Моя реализация должна была поддерживать прокрутку при щелчке по полю, а также прокрутку, когда пользователь нажимает «Далее» на клавиатуре.
У меня был UIView высотой 775. Элементы управления разбросаны в основном группами по 3 человека на большом пространстве. В итоге у меня получился следующий макет IB.
UIView -> UIScrollView -> [UI Components]
А вот и хитрость
Я установил высоту UIScrollView на 500 единиц больше, чем фактический макет (1250). Затем я создал массив с абсолютными позициями, к которым мне нужно прокрутить, и простую функцию для их получения на основе номера тега IB.
static NSInteger stepRange[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};
NSInteger getScrollPos(NSInteger i) {
if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
return 0 ;
return stepRange[i] ;
}
Теперь все, что вам нужно сделать, это использовать следующие две строки кода в textFieldDidBeginEditing и textFieldShouldReturn (последнее, если вы создаете следующую навигацию по полю)
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
Пример.
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
}
else{
[textField resignFirstResponder];
}
return YES ;
}
Этот метод не выполняет «прокрутку назад», как другие методы. Это не было требованием. Опять же, это было для довольно «высокого» UIView, и у меня не было дней, чтобы изучить механизмы внутренней компоновки.
У меня также было много проблем с UIScrollView
составление нескольких UITextFields
, одно или несколько из которых будут закрыты клавиатурой при редактировании.
Вот некоторые вещи, которые следует учитывать, если ваш UIScrollView
не прокручивается должным образом.
1) Убедитесь, что ваш contentSize больше, чем размер кадра UIScrollView
. Способ понять UIScrollViews
состоит в том, что UIScrollView
подобен окну просмотра содержимого, определенного в contentSize. Поэтому, когда для UIScrollview
прокрутка в любом месте, contentSize должен быть больше, чем UIScrollView
. В противном случае прокрутка не требуется, поскольку все, что определено в contentSize, уже отображается. Кстати, contentSize по умолчанию = CGSizeZero
.
2) Теперь, когда вы понимаете, что UIScrollView
на самом деле является окном в ваше «содержимое», это способ убедиться, что клавиатура не закрывает «окно» просмотра UIScrollView
было бы изменить размер UIScrollView
так, чтобы при наличии клавиатуры у вас было окно UIScrollView
размером только с исходным UIScrollView
frame.size.height минус высота клавиатуры. Это гарантирует, что ваше окно будет иметь только ту небольшую видимую область.
3) Вот загвоздка: когда я впервые реализовал это, я решил, что мне нужно получить CGRect
отредактированного текстового поля и вызвать метод scrollRecToVisible UIScrollView. Я реализовал метод
UITextFieldDelegate
textFieldDidBeginEditing
с вызовом метода scrollRecToVisible
. На самом деле это сработало со странным побочным эффектом: прокрутка привязывала UITextField
к месту. Долгое время я не мог понять, что это было. Затем я закомментировал метод делегирования textFieldDidBeginEditing
, и все заработало !! (???). Как оказалось, я считаю, что UIScrollView
на самом деле неявно переносит редактируемый в настоящее время UITextField
в видимое окно. Моя реализация метода UITextFieldDelegate
и последующий вызов метода scrollRecToVisible
были избыточными и были причиной странного побочного эффекта.
Итак, вот шаги, чтобы правильно прокрутить ваш UITextField
в UIScrollView
на место, когда появится клавиатура.
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.view.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.view.window];
keyboardIsShown = NO;
//make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
CGSize scrollContentSize = CGSizeMake(320, 345);
self.scrollView.contentSize = scrollContentSize;
}
- (void)keyboardWillHide:(NSNotification *)n
{
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// resize the scrollview
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height += (keyboardSize.height - kTabBarHeight);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = NO;
}
- (void)keyboardWillShow:(NSNotification *)n
{
// This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown. This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`. If we were to resize the `UIScrollView` again, it would be disastrous. NOTE: The keyboard notification will fire even when the keyboard is already shown.
if (keyboardIsShown) {
return;
}
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// resize the noteView
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = YES;
}
viewDidLoad
viewDidUnload
contentSize
установлен и больше, чем ваш UIScrollView
в viewDidLoad
UIScrollView
при наличии клавиатуры UIScrollView
, когда клавиатура отключится. UITextField
вкладывается, даже если клавиатура уже присутствует, чтобы избежать сжатия ] UIScrollView
, когда он уже сжался Следует отметить, что UIKeyboardWillShowNotification
срабатывает, даже если клавиатура уже находится на экране, когда вы нажимаете другой UITextField
. Я позаботился об этом, используя ivar, чтобы избежать изменения размера UIScrollView
, когда клавиатура уже находится на экране. Случайное изменение размера UIScrollView
, когда клавиатура уже там, будет иметь катастрофические последствия!
Надеюсь, этот код избавит некоторых из вас от головной боли.
TextField
Это переместит представление достаточно только, чтобы не скрывать только активный TextField.
struct ContentView: View {
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 3)
@State private var name = Array<String>.init(repeating: "", count: 3)
var body: some View {
VStack {
Group {
Text("Some filler text").font(.largeTitle)
Text("Some filler text").font(.largeTitle)
}
TextField("text #1", text: $name[0], onEditingChanged: { if [110] { self.kGuardian.showField = 0 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[0]))
TextField("text #2", text: $name[1], onEditingChanged: { if [110] { self.kGuardian.showField = 1 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[1]))
TextField("text #3", text: $name[2], onEditingChanged: { if [110] { self.kGuardian.showField = 2 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[2]))
}.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
}
}
TextField
sЭто перемещается весь текстовое поле, если клавиатура появляется для какого-либо из них. Но только если необходимый. Если клавиатура не скроет текстовые поля, то они не переместятся.
struct ContentView: View {
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 1)
@State private var name = Array<String>.init(repeating: "", count: 3)
var body: some View {
VStack {
Group {
Text("Some filler text").font(.largeTitle)
Text("Some filler text").font(.largeTitle)
}
TextField("enter text #1", text: $name[0])
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("enter text #2", text: $name[1])
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("enter text #3", text: $name[2])
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[0]))
}.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
}
}
Оба примера используют те же общие коды: GeometryGetter и KeyboardGuardian, Вдохновленный @kontiki
, Это - представление, которое поглощает размер и положение его родительского представления. Инкапсулируют описание здесь для достижения этого, это называют в .background модификаторе. Это - очень мощный модификатор, не только способ украсить фон представления. При передаче представления .background (MyView ()), MyView получает измененное представление как родителя. Используя GeometryReader то, что позволяет представлению знать геометрию родителя.
, Например: Text("hello").background(GeometryGetter(rect: $bounds))
заполнит переменные границы, с размером и положением текстового представления и использования пространства глобальной координаты.
struct GeometryGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { geometry in
Group { () -> AnyView in
DispatchQueue.main.async {
self.rect = geometry.frame(in: .global)
}
return AnyView(Color.clear)
}
}
}
}
Примечание, которое эти DispatchQueue.main.async
должно избежать возможности изменения состояния представления, в то время как это представляется.
цель KeyboardGuardian, должен отслеживать клавиатуру, показывают/скрывают события и вычисляют, сколько пространства представление должно быть смещено.
Примечание, что это обновляет слайд, когда пользовательские вкладки от одного поля до другого*
import SwiftUI
import Combine
final class KeyboardGuardian: ObservableObject {
public var rects: Array<CGRect>
public var keyboardRect: CGRect = CGRect()
// keyboardWillShow notification may be posted repeatedly,
// this flag makes sure we only act once per keyboard appearance
public var keyboardIsHidden = true
@Published var slide: CGFloat = 0
var showField: Int = 0 {
didSet {
updateSlide()
}
}
init(textFieldCount: Int) {
self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyBoardWillShow(notification: Notification) {
if keyboardIsHidden {
keyboardIsHidden = false
if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
keyboardRect = rect
updateSlide()
}
}
}
@objc func keyBoardDidHide(notification: Notification) {
keyboardIsHidden = true
updateSlide()
}
func updateSlide() {
if keyboardIsHidden {
slide = 0
} else {
let tfRect = self.rects[self.showField]
let diff = keyboardRect.minY - tfRect.maxY
if diff > 0 {
slide += diff
} else {
slide += min(diff, 0)
}
}
}
}