Если честно, я не в восторге от любого из предложенных здесь решений. Я нашел хороший способ использовать TapGestureRecognizer, который, я думаю, доходит до сути вашей проблемы: когда вы нажимаете на что-либо, кроме клавиатуры, отклоняйте клавиатуру.
В viewDidLoad зарегистрируйтесь для получения уведомлений клавиатуры и создайте UITapGestureRecognizer:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(keyboardWillShow:) name:
UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:@selector(keyboardWillHide:) name:
UIKeyboardWillHideNotification object:nil];
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(didTapAnywhere:)];
Добавьте отображение клавиатуры / скрыть респонденты. Там вы добавляете и удаляете TapGestureRecognizer в UIView, который должен отпускать клавиатуру при нажатии. Примечание. Нет необходимости добавлять его во все подэлементы или элементы управления.
-(void) keyboardWillShow:(NSNotification *) note {
[self.view addGestureRecognizer:tapRecognizer];
}
-(void) keyboardWillHide:(NSNotification *) note
{
[self.view removeGestureRecognizer:tapRecognizer];
}
TapGestureRecognizer вызовет вашу функцию, когда он нажмет, и вы можете отклонить клавиатуру следующим образом:
-(void)didTapAnywhere: (UITapGestureRecognizer*) recognizer {
[textField resignFirstResponder];
}
Хорошая вещь о том, что это решение состоит в том, что оно фильтрует только для отводов, а не для считывания. Поэтому, если у вас есть прокрутка контента над клавиатурой, пролистывание все равно будет прокручиваться и клавиатура будет отображаться. Удалив распознаватель жестов после того, как клавиатура исчезнет, будущие касания на вашем экране обрабатываются нормально.
Здесь много хороших советов. В итоге я использовал метод, написанный Марком в , ответ на другой вопрос . Немного переформатировал его и добавил еще один метод, чтобы упростить его использование «извне». Вот результат:
private static string numberPattern = " ({0})";
public static string NextAvailableFilename(string path)
{
// Short-cut if already available
if (!File.Exists(path))
return path;
// If path has extension then insert the number pattern just before the extension and return next filename
if (Path.HasExtension(path))
return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path)), numberPattern));
// Otherwise just append the pattern to the path and return next filename
return GetNextFilename(path + numberPattern);
}
private static string GetNextFilename(string pattern)
{
string tmp = string.Format(pattern, 1);
if (tmp == pattern)
throw new ArgumentException("The pattern must include an index place-holder", "pattern");
if (!File.Exists(tmp))
return tmp; // short-circuit if no matches
int min = 1, max = 2; // min is inclusive, max is exclusive/untested
while (File.Exists(string.Format(pattern, max)))
{
min = max;
max *= 2;
}
while (max != min + 1)
{
int pivot = (max + min) / 2;
if (File.Exists(string.Format(pattern, pivot)))
min = pivot;
else
max = pivot;
}
return string.Format(pattern, max);
}
Пока тестировалось только частично, но обновлю, если обнаружу какие-либо ошибки. ( Код Марка отлично работает!) Если вы обнаружите какие-либо проблемы с ним, прокомментируйте, отредактируйте или что-то в этом роде :)
public FileInfo MakeUnique(string path)
{
string dir = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
for (int i = 1; ;++i) {
if (!File.Exists(path))
return new FileInfo(path);
path = Path.Combine(dir, fileName + " " + i + fileExt);
}
}
Очевидно, что это уязвимо для условий гонки, как указано в других ответах.
Обратите внимание на методы в классе Path , в частности на Path.GetFileNameWithoutExtension () и Path.GetExtension () .
Вы даже можете найти Path.GetRandomFileName () полезным!
Редактировать:
Раньше я пытался записать файл (с моим желаемое имя), а затем с помощью вышеуказанных функций создать новое имя, если выбрасывается соответствующее IOException
, повторение до успешного завершения.
Это всего лишь строковая операция; найдите место в строке имени файла, куда вы хотите вставить номер, и воссоздайте новую строку с вставленным номером. Чтобы сделать его пригодным для повторного использования, вы можете найти число в этом месте и преобразовать его в целое число, чтобы его можно было увеличить.
Обратите внимание, что в общем случае это так. создание уникального имени файла небезопасно; существуют очевидные опасности состояния гонки .
В платформе могут быть готовые решения для этого, я плохо разбираюсь в C #, поэтому я не могу здесь помочь.
Идея состоит в том, чтобы получить список существующих файлов, проанализировать числа, а затем сделать следующий по величине.
Примечание: это уязвимо для условий гонки, поэтому, если у вас есть несколько потоков, создающих эти файлы, будьте осторожны .
Примечание 2: это не проверено.
public static FileInfo GetNextUniqueFile(string path)
{
//if the given file doesn't exist, we're done
if(!File.Exists(path))
return new FileInfo(path);
//split the path into parts
string dirName = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
//get the directory
DirectoryInfo dir = new DirectoryInfo(dir);
//get the list of existing files for this name and extension
var existingFiles = dir.GetFiles(Path.ChangeExtension(fileName + " *", fileExt);
//get the number strings from the existing files
var NumberStrings = from file in existingFiles
select Path.GetFileNameWithoutExtension(file.Name)
.Remove(0, fileName.Length /*we remove the space too*/);
//find the highest existing number
int highestNumber = 0;
foreach(var numberString in NumberStrings)
{
int tempNum;
if(Int32.TryParse(numberString, out tempnum) && tempNum > highestNumber)
highestNumber = tempNum;
}
//make the new FileInfo object
string newFileName = fileName + " " + (highestNumber + 1).ToString();
newFileName = Path.ChangeExtension(fileName, fileExt);
return new FileInfo(Path.Combine(dirName, newFileName));
}
/// <summary>
/// Create a unique filename for the given filename
/// </summary>
/// <param name="filename">A full filename, e.g., C:\temp\myfile.tmp</param>
/// <returns>A filename like C:\temp\myfile633822247336197902.tmp</returns>
public string GetUniqueFilename(string filename)
{
string basename = Path.Combine(Path.GetDirectoryName(filename),
Path.GetFileNameWithoutExtension(filename));
string uniquefilename = string.Format("{0}{1}{2}",
basename,
DateTime.Now.Ticks,
Path.GetExtension(filename));
// Thread.Sleep(1); // To really prevent collisions, but usually not needed
return uniquefilename;
}
Поскольку DateTime.Ticks имеет разрешение 100 наносекунд , коллизии крайне маловероятны. Тем не менее, Thread.Sleep (1) обеспечит это, но я сомневаюсь, что это необходимо
Если формат вас не беспокоит, вы можете позвонить:
try{
string tempFile=System.IO.Path.GetTempFileName();
string file=System.IO.Path.GetFileName(tempFile);
//use file
System.IO.File.Delete(tempFile);
}catch(IOException ioe){
//handle
}catch(FileIOPermission fp){
//handle
}
PS: - Пожалуйста, прочтите больше об этом на msdn перед использованием.
Если проверка существования файла слишком сложна, вы всегда можете просто добавить дату и время к имени файла, чтобы сделать его уникальным:
FileName.YYYYMMDD.HHMMSS
Может быть, даже при необходимости добавьте миллисекунды.
Вместо того, чтобы несколько раз тыкать в диск, чтобы выяснить, есть ли на нем конкретный вариант желаемого имени файла, вы можете запросить список уже существующих файлов и найти первый пробел в соответствии с вашим алгоритмом.
public static class FileInfoExtensions
{
public static FileInfo MakeUnique(this FileInfo fileInfo)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
string newfileName = new FileUtilities().GetNextFileName(fileInfo.FullName);
return new FileInfo(newfileName);
}
}
public class FileUtilities
{
public string GetNextFileName(string fullFileName)
{
if (fullFileName == null)
{
throw new ArgumentNullException("fullFileName");
}
if (!File.Exists(fullFileName))
{
return fullFileName;
}
string baseFileName = Path.GetFileNameWithoutExtension(fullFileName);
string ext = Path.GetExtension(fullFileName);
string filePath = Path.GetDirectoryName(fullFileName);
var numbersUsed = Directory.GetFiles(filePath, baseFileName + "*" + ext)
.Select(x => Path.GetFileNameWithoutExtension(x).Substring(baseFileName.Length))
.Select(x =>
{
int result;
return Int32.TryParse(x, out result) ? result : 0;
})
.Distinct()
.OrderBy(x => x)
.ToList();
var firstGap = numbersUsed
.Select((x, i) => new { Index = i, Item = x })
.FirstOrDefault(x => x.Index != x.Item);
int numberToUse = firstGap != null ? firstGap.Item : numbersUsed.Count;
return Path.Combine(filePath, baseFileName) + numberToUse + ext;
}
}