ios - development - submit app on app store




Guias de layout de área segura em arquivos xib-iOS 10 (6)

Comecei a adaptar meu aplicativo para iPhone X e encontrei um problema no Interface Builder. Os guias de layout de área segura devem ser compatíveis com versões anteriores, de acordo com vídeos oficiais da Apple. Eu achei que funciona muito bem em storyboards.

Mas nos meus arquivos XIB, os guias de layout de área segura não são respeitados no iOS 10.

Eles funcionam bem para a nova versão do SO, mas os dispositivos do iOS 10 parecem simplesmente assumir a distância da área segura como zero (ignorando o tamanho da barra de status).

Estou faltando alguma configuração necessária? É um bug do Xcode e, em caso afirmativo, quaisquer soluções conhecidas?

Aqui está uma captura de tela do problema em um projeto de teste (esquerda iOS 10, direita iOS 11):


Acabei de excluir a restrição para a área segura que eu tinha no meu arquivo xib. Em vez disso, fiz uma saída para o UIView em questão e, a partir do código, anexei-o assim, em ViewDidLayoutSubviews.

let constraint = alert.viewContents.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 0)
constraint.priority = 998
constraint.isActive = true

Isso liga um pequeno "alerta" ao topo da tela, mas garante que a visualização do conteúdo dentro do alerta esteja sempre abaixo da área de segurança superior (iOS11ish) / topLayoutGuide (iOS10ish)

Solução simples e única. Se algo quebrar, eu voltarei.


Atualmente, a compatibilidade com versões anteriores não funciona bem.

Minha solução é criar duas restrições no construtor de interfaces e remover uma, dependendo da versão do ios que você está usando:

  • para ios 11: view.top == safe area.top
  • para versões anteriores: view.top == superview.top + 20

Adicione ambos como tomadas como myConstraintSAFEAREA e myConstraintSUPERVIEW respectivamente. Então:

override func viewDidLoad() {
    if #available(iOS 11.0, *) {
        view.removeConstraint(myConstraintSUPERVIEW)
    } else {
        view.removeConstraint(myConstraintSAFEAREA)
    }
}

Eu adicionei uma subclasse NSLayoutConstraint para corrigir esse problema ( IBAdjustableConstraint ), com uma variável @IBInspectable , se parece com isso.

class IBAdjustableConstraint: NSLayoutConstraint {

    @IBInspectable var safeAreaAdjustedConstant: CGFloat = 0 {
        didSet {
            if OS.TenOrBelow {
                constant += safeAreaAdjustedConstantLegacy
            }
        }
    }
}

E OS.TenOrBelow

struct OS {
    static let TenOrBelow = UIDevice.current.systemVersion.compare("10.9", options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
}

Basta definir isso como a subclasse de sua restrição no IB e você será capaz de fazer <iOS11 alterações específicas. Espero que isso ajude alguém.


Eu combinei algumas das respostas desta página para isso, o que funciona como um encanto (somente para o guia de layout superior, conforme solicitado na pergunta):

  1. Certifique-se de usar área segura em seu storyboard ou arquivo xib
  2. Restringir suas visões às áreas seguras
  3. Para cada view que tenha uma constraint anexada ao SafeArea.top
    • Crie um IBOutlet para a view
    • Crie um IBOutler para a constraint
  4. Dentro do ViewController no viewDidLoad:

    if (@available(iOS 11.0, *)) {}
    else {
        // For each view and constraint do:
        [self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
        self.constraint.active = NO;
    }

Editar:

Aqui está a versão melhorada que acabei usando em nossa base de código. Basta copiar / colar o código abaixo e conectar cada visualização e restrições à sua IBOutletCollection.

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *constraintsAttachedToSafeAreaTop;
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *viewsAttachedToSafeAreaTop;


if (@available(iOS 11.0, *)) {}
else {
    for (UIView *viewAttachedToSafeAreaTop in self.viewsAttachedToSafeAreaTop) {
        [viewAttachedToSafeAreaTop.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
    }
    for (NSLayoutConstraint *constraintAttachedToSafeAreaTop in self.constraintsAttachedToSafeAreaTop) {
        constraintAttachedToSafeAreaTop.active = NO;
    }
}

A contagem de cada IBOutletCollection deve ser igual. por exemplo, para cada visão deve haver sua restrição associada


Existem alguns problemas com layout de área segura e compatibilidade com versões anteriores. Veja meu comentário here .

Você pode conseguir contornar os problemas com restrições adicionais, como uma prioridade 1000> = 20.0 para superview.top e uma prioridade de 750 == safearea.top. Se você sempre mostra uma barra de status, isso deve consertar as coisas.

Uma abordagem melhor pode ser ter storyboards / xibs separados para o pré-iOS 11 e iOS-11 e superiores, especialmente se você tiver mais problemas do que isso. A razão que é preferível é que, antes do iOS 11, você deve restringir o layout aos guias de layout superior e inferior, mas, para o iOS 11, você deve colocá-los em áreas seguras. Guias de layout sumiram. A disposição dos guias de layout para o pré-iOS 11 é estilisticamente melhor do que apenas compensar por um mínimo de 20 pixels, embora os resultados sejam os mesmos do IFF, você sempre mostra uma barra de status.

Se você adotar essa abordagem, precisará configurar cada arquivo para o destino de implementação correto no qual ele será usado (iOS 11 ou algo anterior) para que o Xcode não forneça avisos e permita o uso de guias de layout ou áreas seguras, dependendo. Em seu código, verifique o iOS 11 em tempo de execução e, em seguida, carregue o storyboard / xibs apropriado.

A desvantagem dessa abordagem é a manutenção (você terá dois conjuntos de controladores de visualização para manter e manter em sincronia), mas quando o aplicativo oferecer suporte apenas ao iOS 11 ou superior, a Apple corrigirá a geração de restrições do guia de layout com compatibilidade reversa. livrar das versões pré-iOS 11.

By the way, como você está exibindo o controlador que você está vendo isso com? É apenas o controlador de visão raiz ou você o apresentou, ou ..? A questão que notei tem a ver com os controladores de visualização, por isso você pode estar acertando um caso diferente.


Isso também funciona:

override func viewDidLoad() {
    super.viewDidLoad()

    if #available(iOS 11.0, *) {}
    else {
        view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height - 80).isActive = true
        view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20).isActive = true
    }
}




iphone-x