Niestandardowe rozmiary czcionek w klasach rozmiaru Xcode 6 nie działają poprawnie z czcionkami niestandardowymi

104

Xcode 6 ma nową funkcję, w której czcionki i rozmiary czcionek są w UILabel, UITextFieldi UIButtonmogą być ustawiane automatycznie na podstawie klasy rozmiaru bieżącej konfiguracji urządzenia, bezpośrednio w serii ujęć. Na przykład możesz ustawić UILabelczcionkę w rozmiarze 12 w konfiguracjach o dowolnej szerokości i niewielkiej wysokości (np. Na iPhone'ach w orientacji poziomej), a rozmiar 18 na konfiguracjach o zwykłej szerokości i wysokości (np. Na iPadach ). Więcej informacji można znaleźć tutaj:

developer.apple.com/size_class

W teorii jest to świetna funkcja, ponieważ może sprawić, że nie będzie konieczne programowe ustawianie różnych czcionek w funkcjach interfejsu użytkownika na podstawie konfiguracji urządzenia. W tej chwili mam kod warunkowy, który ustawia czcionki na podstawie typu urządzenia, ale oczywiście oznacza to, że muszę ustawić czcionki programowo w całej aplikacji. Więc początkowo byłem bardzo podekscytowany tą funkcją, ale odkryłem, że ma ona poważny problem z rzeczywistym użyciem (być może błąd). Zwróć uwagę, że tworzę w oparciu o SDK 8 i ustawiam minimalny cel wdrożenia iOS 8 , więc nie ma to nic wspólnego ze zgodnością ze starymi wersjami iOS.

Problem jest następujący: jeśli ustawię różne rozmiary czcionek dla różnych klas wielkości i użyję czcionki „systemowej” dostarczonej przez iOS , wszystko działa zgodnie z oczekiwaniami, a rozmiary czcionek zmieniają się w zależności od klasy rozmiaru. Jeśli używam niestandardowej czcionki dostarczonej przez moją aplikację (tak, mam ją poprawnie skonfigurowaną w moim pakiecie aplikacji, ponieważ działa programowo) i ustawię niestandardową czcionkę na etykiecie w scenorysie XCode 6 , która również działa zgodnie z oczekiwaniami. Ale kiedy próbuję użyć różnych rozmiarów niestandardowej czcionki dla różnych klas rozmiarów, w scenorysie nagle nie działa. Jedyną różnicą w konfiguracji jest wybrana przeze mnie czcionka (niestandardowa w porównaniu z czcionką systemową). Zamiast tego wszystkie czcionki pojawiają się w plikuurządzenie i symulator jako domyślna czcionka systemowa w domyślnym rozmiarze , niezależnie od klasy rozmiaru (i sprawdziłem za pomocą debugera, że ​​zastępuje czcionkę systemową rzeczywistą określoną w scenorysie). Zasadniczo funkcja klasy rozmiaru wydaje się być zepsuta dla niestandardowych czcionek. Co ciekawe, niestandardowe czcionki faktycznie wyświetlają się i odpowiednio dostosowują rozmiar w okienku „Podgląd” XCode 6 dla kontrolera widoku: przestaje działać tylko wtedy, gdy działa na rzeczywistym systemie iOS (co sprawia, że ​​myślę, że konfiguruję go poprawnie) .

Wypróbowałem wiele różnych czcionek niestandardowych i wydaje się, że nie działa dla żadnej z nich, ale zawsze działa, jeśli zamiast tego używam „System”.

W każdym razie, czy ktoś inny widział ten problem w Xcode 6 ?

Wszelkie pomysły, czy to błąd w iOS 8, Xcode, czy coś

Robię źle?

Jedynym rozwiązaniem, jakie znalazłem, jak powiedziałem, jest dalsze programowe ustawianie czcionek, tak jak w przypadku około trzech wersji iOS, ponieważ to działa.

Ale chciałbym móc korzystać z tej funkcji, gdybym mógł zmusić ją do pracy z niestandardowymi czcionkami. Korzystanie z czcionki systemowej jest niedopuszczalne w naszym projekcie.


DODATKOWE INFORMACJE: Od Xcode 8.0 błąd został naprawiony.

mnemia
źródło
3
Mogę potwierdzić, że nadal tak jest w wersji sklepu z aplikacjami xCode 6.1 (6A1052d).
jodm
3
Nie jest to rozwiązanie, ale udało mi się obejść problem za pomocą kategorii. Moja kategoria UILabel + Font.h (mam też jedną dla przycisków) zawiera następujący kod. - (void) awakeFromNib {float size = [self.font pointSize]; self.font = [UIFont fontWithName: @ "YourCustomFont" size: size]; } Dzięki temu możemy używać klas wielkości z czcionką systemową do ustawiania wartości punktowych w różnych klasach wielkości, a kategoria zapewnia ustawienie prawidłowej czcionki. Może mógłbyś dać temu szansę, dopóki Apple nie wyda aktualizacji?
Daniel Retief Fourie
2
Nadal nierozwiązane w Xcode 6.3.1. Jestem na to zły.
Fury
7
Nadal nie działa z Xcode 7 final. Nie rozumiem, dlaczego Apple tego nie naprawia.
ernesto
5
nadal nie działa na Xcode 7.2 iOS 9.2
Mojtaba

Odpowiedzi:

41

Szybka naprawa:

1) Ustaw czcionki jako system dla klas wielkości

Inspektor atrybutów etykiet

2) Podklasa UILabel i nadpisanie metody „layoutSubviews”, na przykład:

- (void)layoutSubviews
{
  [super layoutSubviews];

   // Implement font logic depending on screen size
    if ([self.font.fontName rangeOfString:@"bold" options:NSCaseInsensitiveSearch].location == NSNotFound) {
        NSLog(@"font is not bold");
        self.font = [UIFont fontWithName:@"Custom regular Font" size:self.font.pointSize];
    } else {
        NSLog(@"font is bold");
        self.font = [UIFont fontWithName:@"Custom bold Font" size:self.font.pointSize];
    }

}

Nawiasem mówiąc, jest to bardzo wygodna technika w przypadku ikonicznych czcionek

brzytwa28
źródło
Chociaż jest to dobre obejście, jeśli czcionka używana w całej aplikacji jest taka sama, jest to nieco mniej wygodne, jeśli musisz używać różnych czcionek w różnych miejscach, ponieważ wtedy musisz mieć różne podklasy itp. W moim przypadku już mam miał skonfigurowane czcionki programistyczne i można to zrobić na wiele różnych sposobów. Ale celem mojego pytania nie jest pytanie, jak to zrobić programowo: chodzi o to, aby zapytać, dlaczego to nie działa poprawnie przy użyciu Storyboardów (to błąd Apple).
mnemia
@mnemia Zgadzam się. Moim celem jest tylko pokazanie, jak sobie z tym poradzić.
brzytwa 28
1
Działa świetnie. Nie musisz jednak tworzyć podklasy UILabel, możesz po prostu nadpisać tę metodę w widoku zawierającym etykietę i zrobić. W self.label.font = ...ten sposób możesz używać różnych czcionek w różnych miejscach.
KPM
Lub możesz skonfigurować @IBInspectable, aby przechowywać nazwę czcionki, ponownie używać tej samej niestandardowej klasy etykiet i żądać różnych czcionek bezpośrednio w serii ujęć.
Craig Grummitt
Czy istnieje podobne rozwiązanie dla UITextField?
Chris Byatt
17

Po wypróbowaniu wszystkiego ostatecznie zdecydowałem się na kombinację powyższych rozwiązań. Korzystanie z Xcode 7.2, Swift 2.

import UIKit

class LabelDeviceClass : UILabel {

    @IBInspectable var iPhoneSize:CGFloat = 0 {
        didSet {
            if isPhone() {
                overrideFontSize(iPhoneSize)
            }
        }
    }

    @IBInspectable var iPadSize:CGFloat = 0 {
        didSet {
            if isPad() {
                overrideFontSize(iPadSize)
            }
        }
    }

    func isPhone() -> Bool {
        // return UIDevice.currentDevice().userInterfaceIdiom == .Phone
        return !isPad()
    }

    func isPad() -> Bool {
        // return UIDevice.currentDevice().userInterfaceIdiom == .Pad
        switch (UIScreen.mainScreen().traitCollection.horizontalSizeClass, UIScreen.mainScreen().traitCollection.verticalSizeClass) {
        case (.Regular, .Regular):
            return true
        default:
            return false
        }
    }

    func overrideFontSize(fontSize:CGFloat){
        let currentFontName = self.font.fontName
        if let calculatedFont = UIFont(name: currentFontName, size: fontSize) {
            self.font = calculatedFont
        }
    }

}
  • @IBInspectable pozwala ustawić rozmiar czcionki w Storyboard
  • Używa didSetobserwatora, aby uniknąć pułapek layoutSubviews()(nieskończona pętla dla dynamicznych wysokości wierszy widoku tabeli) i awakeFromNib()(patrz komentarz @ cocoaNoob)
  • Używa klas wielkości zamiast idiomu urządzenia, w nadziei, że ostatecznie użyje tego z @IBDesignable
  • Niestety, @IBDesignablenie działa traitCollectionzgodnie z tym innym artykułem na stosie
  • Instrukcja przełączania kolekcji cech jest wykonywana UIScreen.mainScreen()raczej selfna tym artykule stosu niż na tym artykule
Robert Chen
źródło
Najlepsza odpowiedź, ale niestety pod głosowaniem… Dzięki
Rohit Pradhan
9

Obejście dla UILabel: zachować ten sam rozmiar czcionki na wszystkich klas wielkości, ale zamiast zmienić wysokość etykiety odpowiednio w każdej wielkości klasy. Twoja etykieta musi mieć włączoną funkcję automatycznego zmniejszania. W moim przypadku działało ładnie.

Phil
źródło
1
Mogłem zrobić to samo, zmieniając szerokość mojej etykiety w każdej klasie rozmiaru. Dobra wskazówka!
dmzza
1
Nie udało mi się zmniejszyć rozmiaru czcionki, ustawiając mniejszą stałą wysokość dla mojego UILabel. Jak dokładnie sprawiłeś, że działa? adjustsFontSizeToFitWidthwydaje się działać tylko dla szerokości (której nie mogę użyć, ponieważ tekst etykiet jest dynamiczny).
ernesto
Mam ten sam problem z dopasowaniem do szerokości
Jules
1
Skończyło się na ustawieniu ograniczenia wysokości etykiety proporcjonalnie do wysokości nadzoru. Działa to dobrze, o ile ustawisz parametr „Lines” etykiety w IB na 0.
Dorian Roy
8

Ten (i inne związane z Xcode-Size Classes) błąd spowodował ostatnio poważny smutek, ponieważ musiałem przejść przez ogromny plik storyboardu, hakując rzeczy.

Dla każdego innego na tym stanowisku chciałbym dodać coś do odpowiedzi @ razor28, aby złagodzić ból.

W pliku nagłówkowym niestandardowej podklasy użyj IBInspectabledla atrybutów środowiska wykonawczego. Dzięki temu te atrybuty będą dostępne z „Inspektora atrybutów”, wizualnie tuż nad domyślną pozycją ustawień czcionek.

Przykładowe zastosowanie:

@interface MyCustomLabel : UILabel

    @property (nonatomic) IBInspectable NSString *fontType;
    @property (nonatomic) IBInspectable CGFloat iphoneFontSize;
    @property (nonatomic) IBInspectable CGFloat ipadFontSize;

@end

W ten sposób uzyskasz bardzo pomocne wyniki:

wprowadź opis obrazu tutaj

Dodatkową korzyścią jest to, że teraz nie musimy ręcznie dodawać atrybutów środowiska wykonawczego dla każdej etykiety. Jest to najbliższe osiągnięcie zamierzonego zachowania XCode. Miejmy nadzieję, że tego lata nadejdzie właściwa poprawka w iOS 9.

joakim
źródło
Dodałem zgodnie z twoją instrukcją, ale nie jestem w stanie znaleźć wizualnie dostępu do "Inspektora atrybutów" w xib, więc jak to osiągnąć?
Darshan Kunjadiya
3

Rozwiązanie podobne do @ razor28, ale myślę, że jest trochę bardziej uniwersalne. Działa również dobrze na starszych wersjach iOS

https://gist.github.com/softmaxsg/8a3ce30331f9f07f023e

Witalij
źródło
I oczywiście, możesz ustawić właściwość overrideFontName w Interface Builder, co pozwala uniknąć pisania niepotrzebnego kodu
Witalij
To nie zadziałało dla mnie, wymieniłem wszystkie zainstalowane czcionki w powyższej metodzie, a moja czcionka została wymieniona i przypisana do zmiennej, ale nie zmieniłem czcionki pokazanej na etykiecie na symulatorze i zakładam urządzenie
Jules
3

Błąd jest nadal aktualny w XCode 7.0 GM.

Rozwiązanie Razor28 powoduje w niektórych przypadkach nieskończone pętle. Moje doświadczenie polega na używaniu go w połączeniu z SwipeView .

Zamiast tego proponuję:

1) Podklasa UILabel i przesłonięcie setFont:

- (void)setFont:(UIFont *)font
{
    font = [UIFont fontWithName:(@"Montserrat") size:font.pointSize];
    [super setFont:font];
}

2) Ustaw niestandardową klasę swoich etykiet UIL, a następnie ustaw klasy rozmiaru czcionki za pomocą czcionki systemowej

wprowadź opis obrazu tutaj

Foti Dim
źródło
@ Maarten1909 Czy odnosisz się do obejścia lub prostego sposobu XCode? Czy możesz podać pierwszą wersję, więc spróbuję ją odtworzyć?
Foti Dim
Wygląda na to, że cały czas używam niewłaściwej nazwy rodziny czcionek. Okazuje się, że w takim przypadku czcionki są resetowane do czcionki systemowej o rozmiarze 14,0 pint. Mój błąd. Ale może się przydać innym!
Berendschot
0

Nadal występuje problem polegający na tym, że nie można użyć funkcji do ustawiania czcionek dla różnych klas wielkości z poziomu konstruktora interfejsu.

Po prostu ustaw czcionkę w zależności od urządzenia, które chcesz, tak jak poniżej:

if (Your Device is iPAd) //For iPad
{
   [yourLabel setFont:[UIFont fontWithName:@"FontName" size:FontSize]]; 
}
else  //For Other Devices then iPad
{
   [yourLabel setFont:[UIFont fontWithName:@"FontName" size:FontSize]]; 
}

Działa to doskonale na wszystkich urządzeniach.

Nirmit Dagly
źródło
0

Żaden z nich nie działał dla mnie, ale tak się stało. Musisz także użyć czcionki systemowej w IB

#import <UIKit/UIKit.h>

@interface UILabelEx : UILabel


@end

#import "UILabelEx.h"
#import "Constants.h"

@implementation UILabelEx

- (void) traitCollectionDidChange: (UITraitCollection *) previousTraitCollection {
    [super traitCollectionDidChange: previousTraitCollection];

    self.font = [UIFont fontWithName:APP_FONT size:self.font.pointSize];   
}
@end
Jules
źródło
0

Wciąż nie ma podpisanej prawidłowej odpowiedzi. Ten kod działa dobrze dla mnie. Najpierw musisz wyłączyć rozmiar czcionki dla klas rozmiaru w narzędziu do tworzenia interfejsu. W IB możesz używać niestandardowej czcionki.

- (void) traitCollectionDidChange: (UITraitCollection *) previousTraitCollection {
    [super traitCollectionDidChange: previousTraitCollection];

    if ((self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass)
        || self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass) {

         self.textField.font = [UIFont fontWithName:textField.font.fontName size:17.f];

    }
}
Max Smirnov
źródło
0

Pomocne było połączenie niektórych z późniejszych odpowiedzi powyżej. Oto jak rozwiązałem błąd IB za pomocą rozszerzenia Swift UILabel:

import UIKit

// This extension is only required as a work-around to an interface builder bug in XCode 7.3.1
// When custom fonts are set per size class, they are reset to a small system font
// In order for this extension to work, you must set the fonts in IB to System
// We are switching any instances of ".SFUIDisplay-Bold" to "MuseoSans-700" and ".SFUIDisplay-Regular" to "MuseoSans-300" and keeping the same point size as specified in IB

extension UILabel {
    override public func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)

        if ((traitCollection.verticalSizeClass != previousTraitCollection?.verticalSizeClass) || traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass) {
            //let oldFontName = "\(font.fontName)-\(font.pointSize)"

            if (font.fontName == systemFontRegular) {
                font = UIFont(name: customFontRegular, size: (font?.pointSize)!)
                //xlog.debug("Old font: \(oldFontName) -> new Font: \(font.fontName) - \(font.pointSize)")
            }
            else if (font.fontName == systemFontBold) {
                font = UIFont(name: customFontBold, size: (font?.pointSize)!)
                //xlog.debug("Old font: \(oldFontName) -> new Font: \(font.fontName) - \(font.pointSize)")
            }
        }
    }
}
benjam
źródło
-1

Używam Swift, XCode 6.4. Więc to właśnie zrobiłem

import Foundation
import UIKit

    @IBDesignable class ExUILabel: UILabel {

        @IBInspectable var fontName: String = "Default-Font" {
            didSet {
                self.font = UIFont(name: fontName, size:self.font.pointSize)
            }
        }

        override func layoutSubviews() {
            super.layoutSubviews()
            self.font = UIFont(name: fontName, size:self.font.pointSize)
        }
    }
  1. Idź do Projektanta -> Inspektor tożsamości -> Ustaw klasę na ExUILabel

  2. Następnie przejdź do Inspektora atrybutów w projektancie i ustaw nazwę czcionki.

kakopappa
źródło
-1

Wymyśliłem jeszcze szybszą poprawkę (zakładam, że zawsze używasz jednej niestandardowej czcionki).

Utwórz kategorię dla UILabel i dołącz do plików za pomocą błędnego storyboardu z klasami rozmiarów i dedykowanym ustawieniem czcionki dla różnych klas:

@implementation UILabel (FontFixForSizeClassesAppleBug)

- (void)layoutSubviews
{
    [super layoutSubviews];
    if([[UIFont systemFontOfSize:10].familyName isEqualToString:self.font.familyName]) {
        //workaround for interface builder size classes bug which ignores custom font if various classes defined for label: http://stackoverflow.com/questions/26166737/custom-font-sizing-in-xcode6-size-classes-not-working-properly-w-custom-fonts
        self.font = [UIFont fontWithName:@"YOUR_CUSTOM_FONT_NAME" size:self.font.pointSize];
    }
}

@end

Po prostu użyj niestandardowych czcionek w serii ujęć. Gdy błędny interpreter użyje czcionki systemowej zamiast Twojej własnej, ta kategoria przełączy ją na czcionkę niestandardową.

Heps
źródło
-1: Nie jest dobrym pomysłem zastępowanie metod w takiej kategorii jak ta. Powód: stackoverflow.com/questions/5272451/… Zamiast tego powinien mieć swizzle (choć hacky) lub podklasę.
JRG-Developer,
-6

Miałem ten sam problem i znalazłem jeden nie do końca jasny, ale dobre rozwiązanie!

  1. Najpierw ustaw wymagany rozmiar czcionki w serii ujęć z nazwą czcionki systemowej.
  2. Następnie do tej etykiety przypisujesz tag od 100 do 110 (lub więcej, ale 10 zawsze mi wystarczało w jednym kontrolerze widoku).
  3. Następnie umieść ten kod w pliku źródłowym swojego VC i nie zapomnij zmienić nazwy czcionki. Kod szybko.
override func viewDidLayoutSubviews() {
    for i in 100...110 {
        if let label = view.viewWithTag(i) as? UILabel {
            label.font = UIFont(name: "RotondaC-Bold", size: label.font.pointSize)
        }
    }
}
serg_ov
źródło
1
Nie używaj tagów. Użyj IBOutlets. Tagi są złe. Zobacz sesję 231 WWDC 2015: „Jeśli korzystasz z interfejsu API View With Tag lub Set Tag UIView i kodu wysyłkowego, zachęcam Cię do odejścia od tego. Zamiast tego zadeklaruj właściwości swoich klas, a będziesz mieć rzeczywiste połączenia z tymi widokami, których będziesz potrzebować później ”.
KPM
Szkoda, że ​​to rozwiązanie nie jest idealne, ale działa!
serg_ov
-8
  • dodaj czcionki dostarczone przez aplikację do pliku .plist aplikacji
  • dodaj swój plik .ttf do pozycji 0
  • użyj go [UIFont fontWithName: "example.ttf" rozmiar: 10]
user2612184
źródło
To nie jest odpowiedź na ten problem. Oczywiście możesz programowo używać niestandardowych czcionek, ale moje pytanie dotyczy błędu w iOS 8, który uniemożliwia automatyczną zmianę niestandardowych czcionek w zależności od klasy rozmiaru.
mnemia