Bir Xcode projesinde kullanılmayan görüntüler nasıl bulunur?


97

Bir Xcode projesinde kullanılmayan görüntüleri bulmak için tek satır olan var mı? (Tüm dosyalara kodda veya proje dosyalarında isimlerle başvurulduğunu varsayarsak - kod oluşturulmuş dosya adları yoktur.)

Bu dosyalar bir projenin ömrü boyunca birikme eğilimindedir ve herhangi bir png dosyasını silmenin güvenli olup olmadığını söylemek zor olabilir.


4
Bu, XCode4 için de çalışıyor mu? XCode4'teki Cmd-Opt-A "Dosya ekle" iletişim kutusunu açıyor gibi görünüyor.
Rajavanya Subramaniyan

Yanıtlar:


61

Projeye dahil olmayan ancak klasörde takılı kalan dosyalar için

cmd ⌘+ alt ⌥+A

ve grileşmezler.

Ne xib'de ne de kodda referansta bulunulmayan dosyalar için şunun gibi bir şey işe yarayabilir:

#!/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
Hatayla karşılaşırsanız: Böyle bir dosya veya dizin yok, bunun nedeni büyük olasılıkla dosya yolundaki boşluklardır. Tırnakların grep satırına eklenmesi gerekiyor, bu yüzden şöyle: eğer! grep -qhs "$ isim" "$ PROJ";
Lukasz

8
Bunun işe yaramayacağı bir senaryo, görüntüleri adlarını oluşturduktan sonra programlı olarak yükleyebileceğimiz durumdur. Arm1.png, arm2.png .... arm22.png gibi. Adlarını for döngüsü ve yükü içinde oluşturabilirim. Eg Games
Rajavanya Subramaniyan

@ 2x ile adlandırılan retina ekran için görüntüleriniz varsa, bunlar kullanılmamış olarak listelenir. Fazladan bir if-ifadesi ekleyerek bundan kurtulabilirsiniz: if [["$ isim"! = @ 2x ]]; sonra
Sten

3
Cmd + Opt + a artık XCode 5'te çalışmıyor gibi görünüyor. Neyi tetiklemesi gerekiyor?
powtac

cmd + opt + a, projenin bir parçası olsalar bile Images.xcassets'teki dosyaları grileştirmiyor gibi görünmüyor :(
tettoffensive

80

Bu daha sağlam bir çözümdür - herhangi bir metin dosyasında taban adına herhangi bir referans olup olmadığını kontrol eder . Film şeridi dosyalarını içermeyen yukarıdaki çözümlere dikkat edin (tamamen anlaşılabilir, o sırada mevcut değillerdi).

Ack bunu oldukça hızlı yapar, ancak bu komut dosyası sıkça çalışıyorsa yapılması gereken bazı bariz optimizasyonlar vardır. Örneğin, hem retina hem de retina olmayan varlıklara sahipseniz, bu kod her taban adını iki kez kontrol eder.

#!/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'i kurun ve ardından bir brew install ack.
Marko

1
Teşekkürler. Bu cevap aynı zamanda boşluk içeren dosya ve klasörleri de doğru şekilde ele alır.
djskinner

2
@Johnny, dosyayı çalıştırılabilir ( chmod a+x FindUnusedImages.sh) yapmanız , ardından bash'daki diğer herhangi bir program gibi çalıştırmanız gerekir./FindUnusedImages.sh
Mike Sprague 13

2
Pbxproj dosyalarını yok saymak için bir değişiklik yaptım (böylece xcode projesinde bulunan ancak kod veya uçlarda / storyboard'larda kullanılmayan dosyaları yok sayıyorum): result=`ack --ignore-file=match:/.\.pbxproj/ -i "$file"` Bu, ack 2.0 ve üzeri gerektirir
Mike Sprague 13

2
milanpanchal, betiği istediğiniz yere koyabilirsiniz ve sadece görüntüleri aramak için kök olarak kullanmak istediğiniz dizinden (örneğin, proje kök klasörünüz) çalıştırabilirsiniz. Örneğin ~ / script / içine koyabilir ve ardından proje kök klasörünüze gidebilir ve doğrudan betiğe işaret ederek çalıştırabilirsiniz: ~ / script / unused_images.sh
Erik van der Neut

25

Lütfen LSUnusedResources'i deneyin .

Bu, jeffhodnett'in Kullanılmayan filminden büyük ölçüde etkilenmiştir , ancak dürüst olmak gerekirse Kullanılmayan çok yavaştır ve sonuçlar tamamen doğru değildir. Bu yüzden bazı performans optimizasyonları yaptım, arama hızı Kullanılmayanlardan daha hızlı.


2
Vay canına, bu harika bir araç! Bu senaryoları çalıştırmaya çalışmaktan çok daha iyi. Kullanılmayan tüm görselleri görsel olarak görebilir, dilediklerinizi silebilirsiniz. Yine de bulduğum bir şey,
plistte

1
Kesinlikle harika ve günümü kurtar! İplikte en iyi çözüm. Harikasın.
Jakehao

2
İplikte en iyisi. Keşke daha yukarıda olsaydı ve birden fazla oy alabilseydim!
Yoav Schwartz

Buna benzer ancak ölü kod tespiti için bir şey olup olmadığını biliyor musunuz? Örneğin, artık çağrılmayan yöntemler için (en azından artık statik olarak çağrılmaz ).
superpuccio

24

Roman'ın çözümünü denedim ve retina görüntülerini işlemek için birkaç ince ayar ekledim. İyi çalışıyor, ancak görüntü adlarının programlı olarak kodda oluşturulabileceğini ve bu komut dosyasının bu görüntüleri yanlış bir şekilde başvurulmamış olarak listeleyeceğini unutmayın. Örneğin, sahip olabilirsiniz

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

Bu komut dosyası, yanlışlıkla image_1.pngbaşvurulmadığını düşünecektir .

İşte değiştirilmiş komut dosyası:

#!/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 , basename son ekinde ne yapar ?
ThaDon

3
Bilginize, adında boşluk bulunan klasörler komut dosyasıyla ilgili sorunlara neden oluyor.
Steve

3
Hatayla karşılaşırsanız: Böyle bir dosya veya dizin yok, bunun nedeni büyük olasılıkla dosya yolundaki boşluklardır. Tırnakların grep satırına eklenmesi gerekiyor, bu yüzden şöyle: eğer! grep -qhs "$ isim" "$ PROJ";
Lukasz

3
Bu komut dosyası tüm dosyalarımı listeler
jjxtra

2
Neden benim için işe yaramadığını bilmiyorum, bana tüm png resimlerini veriyor
Ömer Obaid

12

İnce deneyebilirsiniz , iyi bir iş çıkarır.

güncelleme: Emcmanus fikriyle devam ettim ve bir makinede ek kurulumdan kaçınmak için küçük bir kullanım oluşturdum.

https://github.com/arun80/xcodeutils


1
İnce ücretli bir uygulamadır. birkaç yanlış pozitiftir ve ticari ürünler için iyi değildir. emcmanus tarafından sağlanan script gerçekten harika.
Arun

6

Yalnızca bu komut dosyası benim için çalışıyor, bu da dosya adlarındaki alanı bile kullanıyor:

Düzenle

swiftDosyaları desteklemek için güncellendi ve cocoapod. Varsayılan olarak, Bölmeler dizinini hariç tutar ve yalnızca proje dosyalarını kontrol eder. --podPods klasörünü de kontrol etmek için çalıştırmak için attrbiute ile çalıştırın :

/.finunusedimages.sh --pod

İşte gerçek komut dosyası:

#!/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

Bu komut dosyası çok fazla işaretlediğiniz kullanılan gibi kaynakları kullanılmayan . İyileştirmeler gerekli.
Artem Shmatkov

Ayrıca büyük, derin proje hiyerarşilerini de sevmez: ./findunused.sh: line 28: / usr / bin / grep: Argüman listesi çok uzun
Martin-Gilles Lavoie

3

Varlık kataloglarını kullanan projeleri idare etmek için @EdMcManus tarafından sağlanan mükemmel yanıtta çok küçük bir değişiklik yaptım.

#!/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

Gerçekten bash betikleri yazmıyorum, bu yüzden burada yapılacak iyileştirmeler varsa (muhtemelen) yorumlarda bana bildirin, ben de güncelleyeceğim.


Dosya adındaki boşluklarla ilgili bir sorunum var. Kodun hemen öncesinde `` IFS = $ '\ n' '(bu dahili alan ayırıcısını yeni satıra ayarlar) ayarlamanın yararlı olduğunu öğrendim - dosyalar yeniden adda yeni satırlara sahipse çalışmaz.
Laura Calinoiu

2

grepKaynak kodunuz olan bir kabuk betiği oluşturabilir ve kurulan görüntüleri proje klasörünüzle karşılaştırabilirsiniz.

İşte adam (lar) GREPveLS

Kolayca tüm kaynak dosyanızı döngüye alabilir, görüntüleri diziye veya buna eşit bir şey kaydedebilir ve kullanabilirsiniz

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

Bu numara ile proje kaynak kodunuzdaki tüm resimleri arayabilirsiniz !!

Bu yardımcı olur umarım!


2

Bir lua senaryosu yazdım, bunu iş yerinde yaptığım için paylaşabileceğimden emin değilim ama iyi çalışıyor. Temelde şunu yapar:

Birinci adım - statik resim referansları (diğer yanıtların kapsadığı kolay kısım)

  • görüntü dizinlerine yinelemeli olarak bakar ve görüntü adlarını çıkarır
  • .png ve @ 2x resim adlarını çıkarır (gerekli değildir / imageNamed'de kullanılmaz :)
  • kaynak dosyalardaki her görüntü adını metinsel olarak arar (dize değişmezinin içinde olmalıdır)

İkinci adım - dinamik resim referansları (eğlenceli bit)

  • kaynakta biçim belirleyicileri içeren tüm dize değişmezlerinin bir listesini çıkarır (örneğin,% @)
  • bu dizelerdeki biçim tanımlayıcılarını normal ifadelerle değiştirir (örneğin, "foo% dbar", "foo [0-9] * bar" olur
  • Bu normal ifade dizelerini kullanarak resim adlarında metinsel olarak arama yapar

Sonra her iki aramada da bulamadığı her şeyi siler.

Uç durum, bir sunucudan gelen görüntü adlarının işlenmemesidir. Bunu halletmek için bu aramaya sunucu kodunu dahil ediyoruz.


Düzgün. Merak ettiğimden, format belirleyicilerini joker karakter regex'lerine dönüştürmek için bir yardımcı program var mı? Tüm belirleyicileri ve platformları doğru bir şekilde yerleştirmek için uğraşmanız gereken çok fazla karmaşıklık olduğunu düşünüyorum. (Biçim belirleyici belgeler)
Ed McManus


2

Diğer yanıtları kullanarak, bu, iki dizindeki görüntüleri nasıl yok sayacağınıza ve pbxproj veya xcassets dosyalarındaki görüntülerin oluşumlarını aramamaya iyi bir örnektir (uygulama simgesine ve açılış ekranlarına dikkat edin). --İgnore-dir = *. Xcassets içindeki * karakterini dizininizle eşleşecek şekilde değiştirin:

#!/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

Bu çerçeveyi kullandım: -

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

Çok iyi çalışıyor! Görüntü adlarının sunucudan gelmesi ve görüntü varlık adının varlık klasörü içindeki görüntünün adından farklı olması sorununu gördüğüm sadece 2 yer ...


Bu, varlıkları aramaz, yalnızca doğrudan referans verilmeyen görüntü dosyaları için. Varlıkları kullanmanız gerektiği gibi kullanıyorsanız, bu araç ne yazık ki sizin için çalışmayacaktır.
Robin Daugherty


0

Kullanılmayan görüntüleri tanımlamak için bir python betiği oluşturdum: 'unused_assets.py' @ gist . Şu şekilde kullanılabilir:

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

Komut dosyasını kullanmanın birkaç kuralı:

  • İlk bağımsız değişken olarak proje klasörü yolunu, ikinci bağımsız değişken olarak varlıklar klasörü yolunu geçirmek önemlidir.
  • Tüm görüntülerin Assets.xcassets klasöründe tutulduğu ve ya hızlı dosyalarda ya da film şeridi içinde kullanıldığı varsayılır.

İlk versiyondaki sınırlamalar:

  • Objektif c dosyaları için çalışmıyor

Geri bildirimlere dayanarak zaman içinde geliştirmeye çalışacağım, ancak ilk sürüm çoğu için iyi olmalı.

Lütfen kodu aşağıda bulabilirsiniz. Her önemli adıma uygun yorumlar eklediğim için kod kendi kendini açıklayıcı olmalıdır .

# 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!")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.