Як знайти невикористані зображення в проекті Xcode?


97

Хтось має один рядок для пошуку невикористаних зображень у проекті Xcode? (Якщо припустити, що на всі файли посилаються імена в коді або файли проекту - імена файлів не генеруються кодом.)

Ці файли, як правило, накопичуються протягом життя проекту, і важко сказати, чи безпечно видалити будь-який PNG.


4
Чи працює це і для XCode4? Cmd-Opt-A у XCode4, здається, відкриває діалогове вікно "Додати файли".
Раджаваня Субраманіян

Відповіді:


61

Для файлів, які не входять до проекту, а просто зависають у папці, ви можете натиснути

cmd ⌘+ alt ⌥+A

і вони не будуть сірим

Для файлів, на які не посилаються ні в xib, ні в коді, щось подібне може працювати:

#!/bin/sh
PROJ=`find . -name '*.xib' -o -name '*.[mh]'`

find . -iname '*.png' | while read png
do
    name=`basename $png`
    if ! grep -qhs "$name" "$PROJ"; then
        echo "$png is not referenced"
    fi
done

6
Якщо ви зіткнулися з помилкою: такого файлу чи каталогу немає, можливо, це пов'язано з пробілами у шляху до файлу. Цитати потрібно додати в греп-рядку, так що це іде: якщо! grep -qhs "$ name" "$ PROJ";
Лукаш

8
Один із варіантів, коли це не вийде, - це коли ми можемо завантажувати зображення програмно після побудови їх імен. Як arm1.png, arm2.png .... arm22.png. Я можу побудувати їх імена в циклі for і load. Напр. Ігри
Раджаванія Субраманіян

Якщо у вас є зображення для відображення сітківки з ім'ям @ 2x, вони відображатимуться як невикористані. Ви можете позбутися цього, додавши додатковий оператор if: if [["$ name"! = @ 2x ]]; потім
Стен

3
Cmd + Opt + a, здається, більше не працює на XCode 5. Що він повинен спрацьовувати?
powtac

cmd + opt + a, здається, не сіріє файли в Images.xcassets, хоча вони є частиною проекту :(
tettoffensive

80

Це більш надійне рішення - він перевіряє будь-яку посилання на базове ім'я в будь-якому текстовому файлі. Зверніть увагу на наведені вище рішення, які не включали файли розкадровки (цілком зрозуміло, їх тоді не існувало).

Ack робить це досить швидко, але є деякі очевидні оптимізації, які потрібно робити, якщо цей сценарій працює часто. Цей код перевіряє кожне базове ім’я двічі, якщо у вас є, наприклад, активи сітківки / неретина.

#!/bin/bash

for i in `find . -name "*.png" -o -name "*.jpg"`; do 
    file=`basename -s .jpg "$i" | xargs basename -s .png | xargs basename -s @2x`
    result=`ack -i "$file"`
    if [ -z "$result" ]; then
        echo "$i"
    fi
done

# Ex: to remove from git
# for i in `./script/unused_images.sh`; do git rm "$i"; done

12
Встановіть Homebrew, а потім виконайте a brew install ack.
Марко

1
Дякую. Ця відповідь також правильно обробляє файли та папки з пробілами.
djskinner

2
@Johnny вам потрібно зробити файл виконуваним ( chmod a+x FindUnusedImages.sh), а потім запустити його, як і будь-яку іншу програму з bash./FindUnusedImages.sh
Mike Sprague

2
Я вніс модифікацію, щоб ігнорувати файли pbxproj (таким чином ігноруючи файли, які знаходяться в проекті xcode, але не використовуються в коді або підказках / раскадровках): Для result=`ack --ignore-file=match:/.\.pbxproj/ -i "$file"` цього потрібно ack 2.0 і вище
Mike Sprague

2
milanpanchal, ти можеш помістити скрипт куди завгодно, і ти просто виконуєш його з будь-якого каталогу, який ти хочеш використовувати як корінь для пошуку зображень (наприклад, кореневу папку проекту). Ви можете помістити його в ~ / script /, наприклад, а потім перейти до кореневої папки вашого проекту та запустити його, вказуючи безпосередньо на сценарій: ~ / script / unused_images.sh
Ерік ван дер Нойт

25

Будь ласка, спробуйте LSUnusedResources .

На нього сильно впливає невикористаний Джефходнетт , але, чесно кажучи, невикористаний дуже повільний, і результати не зовсім коректні. Тож я зробив деяку оптимізацію продуктивності, швидкість пошуку швидша, ніж невикористана.


2
Нічого собі, це чудовий інструмент! Набагато приємніше, ніж намагатися запустити ці сценарії. Ви можете візуально побачити всі невикористані зображення та видалити ті, які бажаєте. Я знайшов одну помилку, хоча вона не забирає зображення, на які посилаються в цьому списку
RyanG

1
Безумовно, приголомшливий і врятуйте мені день! Найкраще рішення в нитці. Ви рок.
Jakehao

2
Найкращий з ниток. Я хотів би, щоб це було вище, і я міг би проголосувати не раз!
Йоав Шварц,

Чи знаєте ви, чи є щось подібне до цього, але для виявлення мертвого коду? Наприклад, для методів, які вже не викликаються (принаймні, більше не називаються статично ).
суперпуччо

24

Я спробував рішення Романа, і я додав кілька налаштувань для обробки зображень сітківки. Це добре працює, але пам’ятайте, що імена зображень можна програмно генерувати в коді, і цей скрипт неправильно перелічить ці зображення як без посилань. Наприклад, ви могли б мати

NSString *imageName = [NSString stringWithFormat:@"image_%d.png", 1];

Цей скрипт неправильно вважатиме, що на нього image_1.pngне посилаються.

Ось модифікований сценарій:

#!/bin/sh
PROJ=`find . -name '*.xib' -o -name '*.[mh]' -o -name '*.storyboard' -o -name '*.mm'`

for png in `find . -name '*.png'`
do
   name=`basename -s .png $png`
   name=`basename -s @2x $name`
   if ! grep -qhs "$name" "$PROJ"; then
        echo "$png"
   fi
done

що робить @ 2x у суфіксальному перемикачі для базового імені?
ThaDon

3
FYI, папки з пробілами в назві викликають проблеми зі сценарієм.
Стів

3
Якщо ви зіткнулися з помилкою: такого файлу чи каталогу немає, можливо, це пов'язано з пробілами у шляху до файлу. Цитати потрібно додати в греп-рядку, так що це іде: якщо! grep -qhs "$ name" "$ PROJ";
Лукаш

3
Цей скрипт перелічує всі мої файли
jjxtra

2
я не знаю, чому він не працює для мене, тому що він дає мені всі зображення у форматі PNG
Омер Обаїд,

12

Можливо, ви можете спробувати струнку , робить гідну роботу.

оновлення: З ідеєю emcmanus я продовжив і створив невеликий утиліт без будь-яких змін, щоб уникнути додаткових налаштувань на машині.

https://github.com/arun80/xcodeutils


1
Струнка - платний додаток. кілька помилкових позитивних результатів і не є корисними для комерційних продуктів. сценарій, наданий emcmanus, справді чудовий.
Арун

6

Для мене працює лише цей скрипт, який навіть обробляє простір у файлах файлів:

Редагувати

Оновлено для підтримки swiftфайлів та cocoapod. За замовчуванням він виключає Pods dir і перевіряє лише файли проекту. Щоб також перевірити папку Pods, запустіть команду --podattrbiute:

/.finunusedimages.sh --pod

Ось власне сценарій:

#!/bin/sh

#varables
baseCmd="find ." 
attrs="-name '*.xib' -o -name '*.[mh]' -o -name '*.storyboard' -o -name '*.mm' -o -name '*.swift'"
excudePodFiles="-not \( -path  */Pods/* -prune \)"
imgPathes="find . -iname '*.png' -print0"


#finalize commands
if [ "$1" != "--pod" ]; then
    echo "Pod files excluded"
    attrs="$excudePodFiles $attrs"
    imgPathes="find . $excudePodFiles -iname '*.png' -print0"
fi

#select project files to check
projFiles=`eval "$baseCmd $attrs"`
echo "Looking for in files: $projFiles"

#check images
eval "$imgPathes" | while read -d $'\0' png
do
   name=`basename -s .png "$png"`
   name=`basename -s @2x $name`
   name=`basename -s @3x $name`

   if grep -qhs "$name" $projFiles; then
        echo "(used - $png)"
   else
        echo "!!!UNUSED - $png"
   fi
done

Цей сценарій позначив занадто багато використаних ресурсів як невикористані . Потрібні вдосконалення.
Артем Шматков

Також не любить великих, глибоких ієрархій проектів: ./findunused.sh: line 28: / usr / bin / grep: Список аргументів занадто довгий
Мартін-Жиль Лавуа

3

Я зробив дуже незначну модифікацію чудової відповіді, наданої @EdMcManus для обробки проектів, що використовують каталоги активів.

#!/bin/bash

for i in `find . -name "*.imageset"`; do
    file=`basename -s .imageset "$i"`
    result=`ack -i "$file" --ignore-dir="*.xcassets"`
    if [ -z "$result" ]; then
        echo "$i"
    fi
done

Я насправді не пишу bash-сценарії, тому, якщо тут будуть внесені вдосконалення (швидше за все), повідомте мене в коментарях, і я оновлю його.


У мене проблема з пробілами в імені файлів. Я з'ясував, що корисно встановити `IFS = $ '\ n' ', безпосередньо перед кодом (цей встановлює внутрішній роздільник поля на новий рядок) - не буде працювати, якщо файли знову мають нові імена рядків.
Laura Calinoiu

2

Ви можете створити сценарій оболонки grepяк вихідний код і порівняти засновані зображення з папкою проекту.

Тут чоловік (и) для GREPіLS

Ви можете легко зациклювати весь свій вихідний файл, зберегти зображення в масиві або щось рівне і використовувати

cat file.m | grep [-V] myImage.png

За допомогою цього фокусу ви можете шукати всі зображення у вихідному коді проекту !!

сподіваюся, що це допомагає!


2

Я написав сценарій lua, я не впевнений, що можу поділитися ним, бо я робив це на роботі, але він працює добре. В основному це робиться так:

Крок перший - посилання на статичні зображення (простий біт, що описується іншими відповідями)

  • рекурсивно переглядає папки зображень і витягує назви зображень
  • знімає імена зображень .png та @ 2x (не потрібно / використовується в imageNamed :)
  • здійснює текстовий пошук кожного імені зображення у вихідних файлах (має бути всередині рядкового літералу)

Крок другий - посилання на динамічні зображення (цікавий біт)

  • витягує список усіх рядкових літералів у джерелі, що містять специфікатори формату (наприклад,% @)
  • замінює специфікатори формату в цих рядках регулярними виразами (наприклад, "foo% dbar" стає "foo [0-9] * bar"
  • здійснює текстовий пошук в іменах зображень, використовуючи ці рядки регулярних виразів

Потім видаляє все, що не було знайдено в жодному пошуку.

Крайній випадок полягає в тому, що імена зображень, що надходять із сервера, не обробляються. Для цього ми включаємо в цей пошук код сервера.


Акуратний. З цікавості є якась утиліта для перетворення специфікаторів формату в регулярні символи? Думаючи, що існує велика кількість складностей, з якими вам доведеться точно впоратися з усіма специфікаторами та платформами. (Документи специфікатора формату)
Ед Макманус,

2

Ви можете спробувати програму FauxPas для Xcode . Це дійсно добре у виявленні відсутніх зображень та безлічі інших питань / порушень, пов’язаних із проектом Xcode.


Схоже, це не оновлювалося з Xcode 9. Можна підтвердити, що він не працює з Xcode 11.
Robin Daugherty

2

Використовуючи інші відповіді, цей приклад є гарним прикладом того, як ігнорувати зображення у двох каталогах та не шукати випадки зображень у файлах pbxproj або xcassets (Будьте обережні з піктограмою програми та заставними екранами). Змініть * у --ignore-dir = *. Xcassets відповідно до вашого каталогу:

#!/bin/bash

for i in `find . -not \( -path ./Frameworks -prune \) -not \( -path ./Carthage -prune \) -not \( -path ./Pods -prune \) -name "*.png" -o -name "*.jpg"`; do 
    file=`basename -s .jpg "$i" | xargs basename -s .png | xargs basename -s @2x | xargs basename -s @3x`
    result=`ack -i --ignore-file=ext:pbxproj --ignore-dir=*.xcassets "$file"`
    if [ -z "$result" ]; then
        echo "$i"
    fi
done

2

Я використав цей фреймворк: -

http://jeffhodnett.github.io/Unused/

Працює блінно добре! Лише в 2 місцях я бачив проблеми, коли імена зображень надходять із сервера та коли назва об’єкта зображення відрізняється від назви зображення всередині папки об’єктів ...


Це не шукає об’єкти, а лише файли зображень, на які немає прямих посилань. Якщо ви використовуєте активи, як слід, цей інструмент, на жаль, не працюватиме для вас.
Робін Догерті


0

Я створив скрипт python для ідентифікації невикористаних зображень: 'unused_assets.py' @ gist . Його можна використовувати так:

python3 unused_assets.py '/Users/DevK/MyProject' '/Users/DevK/MyProject/MyProject/Assets/Assets.xcassets'

Ось кілька правил використання сценарію:

  • Важливо передати шлях папки проекту як перший аргумент, шлях папки активів як другий аргумент
  • Передбачається, що всі зображення зберігаються в папці Assets.xcassets і використовуються або в швидких файлах, або в раскадровках

Обмеження в першій версії:

  • Не працює для об’єктивних файлів c

Я спробую вдосконалити її з часом, спираючись на зворотній зв'язок, проте перша версія повинна бути хорошою для більшості.

Знайдіть нижче код. Код повинен бути зрозумілим, оскільки я додав відповідні коментарі до кожного важливого кроку .

# Usage e.g.: python3 unused_assets.py '/Users/DevK/MyProject' '/Users/DevK/MyProject/MyProject/Assets/Assets.xcassets'
# It is important to pass project folder path as first argument, assets folder path as second argument
# It is assumed that all the images are maintained within Assets.xcassets folder and are used either within swift files or within storyboards

"""
@author = "Devarshi Kulshreshtha"
@copyright = "Copyright 2020, Devarshi Kulshreshtha"
@license = "GPL"
@version = "1.0.1"
@contact = "kulshreshtha.devarshi@gmail.com"
"""

import sys
import glob
from pathlib import Path
import mmap
import os
import time

# obtain start time
start = time.time()

arguments = sys.argv

# pass project folder path as argument 1
projectFolderPath = arguments[1].replace("\\", "") # replacing backslash with space
# pass assets folder path as argument 2
assetsPath = arguments[2].replace("\\", "") # replacing backslash with space

print(f"assetsPath: {assetsPath}")
print(f"projectFolderPath: {projectFolderPath}")

# obtain all assets / images 
# obtain paths for all assets

assetsSearchablePath = assetsPath + '/**/*.imageset'  #alternate way to append: fr"{assetsPath}/**/*.imageset"
print(f"assetsSearchablePath: {assetsSearchablePath}")

imagesNameCountDict = {} # empty dict to store image name as key and occurrence count
for imagesetPath in glob.glob(assetsSearchablePath, recursive=True):
    # storing the image name as encoded so that we save some time later during string search in file 
    encodedImageName = str.encode(Path(imagesetPath).stem)
    # initializing occurrence count as 0
    imagesNameCountDict[encodedImageName] = 0

print("Names of all assets obtained")

# search images in swift files
# obtain paths for all swift files

swiftFilesSearchablePath = projectFolderPath + '/**/*.swift' #alternate way to append: fr"{projectFolderPath}/**/*.swift"
print(f"swiftFilesSearchablePath: {swiftFilesSearchablePath}")

for swiftFilePath in glob.glob(swiftFilesSearchablePath, recursive=True):
    with open(swiftFilePath, 'rb', 0) as file, \
        mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as s:
        # search all the assests within the swift file
        for encodedImageName in imagesNameCountDict:
            # file search
            if s.find(encodedImageName) != -1:
                # updating occurrence count, if found 
                imagesNameCountDict[encodedImageName] += 1

print("Images searched in all swift files!")

# search images in storyboards
# obtain path for all storyboards

storyboardsSearchablePath = projectFolderPath + '/**/*.storyboard' #alternate way to append: fr"{projectFolderPath}/**/*.storyboard"
print(f"storyboardsSearchablePath: {storyboardsSearchablePath}")
for storyboardPath in glob.glob(storyboardsSearchablePath, recursive=True):
    with open(storyboardPath, 'rb', 0) as file, \
        mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as s:
        # search all the assests within the storyboard file
        for encodedImageName in imagesNameCountDict:
            # file search
            if s.find(encodedImageName) != -1:
                # updating occurrence count, if found
                imagesNameCountDict[encodedImageName] += 1

print("Images searched in all storyboard files!")
print("Here is the list of unused assets:")

# printing all image names, for which occurrence count is 0
print('\n'.join({encodedImageName.decode("utf-8", "strict") for encodedImageName, occurrenceCount in imagesNameCountDict.items() if occurrenceCount == 0}))

print(f"Done in {time.time() - start} seconds!")

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.