Вместо того, чтобы использовать обычную кнопку, я создал подкласс UIControl, потому что мне нужно было добавить к ней градиент. У меня также есть способ добавить тень и индикатор активности (не виден на изображении ниже) в качестве кнопки с отслеживанием состояния, чтобы пользователи не нажимали на кнопку, если (например) выполняется вызов API.
Было очень сложно попытаться заставить UIControl вращаться, и чтобы иметь возможность сделать это, я добавил тень как отдельный вид к представлению-контейнеру, содержащему UIControl, чтобы можно было добавить тень.
Теперь проблема в том, что элемент управления ведет себя не совсем так, как вид при вращении — позвольте мне показать вам снимок экрана для контекста:

Это среднее вращение, но его почти видно глазу — изображение показывает, что Градиент составляет 75% длины синего UIView на изображении.
https://github.com/stevencurtis/statefulbutton
Чтобы выполнить это вращение, я удаляю shadowview, а затем изменяю рамку рамки градиента на ее границы, и это проблема.
func viewRotated() {
CATransaction.setDisableActions(true)
shadowView!.removeFromSuperview()
shadowView!.frame = self.frame
shadowView!.layer.masksToBounds = false
shadowView!.layer.shadowOffset = CGSize(width: 0, height: 3)
shadowView!.layer.shadowRadius = 3
shadowView!.layer.shadowOpacity = 0.3
shadowView!.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 20, height: 20)).cgPath
shadowView!.layer.shouldRasterize = true
shadowView!.layer.rasterizationScale = UIScreen.main.scale
self.gradientViewLayer.frame = self.bounds
self.selectedViewLayer.frame = self.bounds
CATransaction.commit()
self.insertSubview(shadowView!, at: 0)
}
Итак, этот метод вращения вызывается через родительский контроллер представления:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { context in
context.viewController(forKey: UITransitionContextViewControllerKey.from)
//inform the loginButton that it is being rotated
self.loginButton.viewRotated()
}, completion: { context in
// can call here when completed the transition
})
}
Я знаю, что это проблема, и я думаю, что это происходит не в самое подходящее время, чтобы действовать так же, как UIView. Теперь проблема в том, что я пробовал много вещей, чтобы заставить это работать, и мое лучшее решение (выше) не совсем там.
Бесполезно предлагать использовать UIButton, использовать изображение для градиента (пожалуйста, не предлагайте использовать градиентное изображение в качестве фона для UIButton, я пробовал это) или стороннюю библиотеку. Это моя работа, она работает, но работает неприемлемо для меня, и я хочу, чтобы она работала так же, как и обычный вид (или, по крайней мере, знаю, почему нет). Я также пробовал другие решения, описанные выше, и выбрал свое собственное UIControl. Я знаю, что могу заблокировать представление, если есть вызов API, или использовать другие способы, чтобы пользователь не нажимал кнопку слишком много раз. Я пытаюсь исправить свое решение, а не изобретать способы обойти эту проблему с помощью CAGradientLayer.
Эта проблема: Мне нужно сделать так, чтобы UIControlView с CAGradientLayer фоном вращался так же, как UIView, и не отображать проблему, показанную на изображении выше.
Полный пример: https://github.com/stevencurtis/statefulbutton
Вы можете добавить градиент к UIButton? Есть пример?
да. Вы можете добавить градиентный слой к любому представлению со слоем, включая кнопки.
пример: gist.github.com/Elhoej/1a46f9f504afb18b35809099aa937af3
Вы также можете добавить тени к UIButtons и включить/отключить их, чтобы пользователи не рассылали их спамом.
Итак, в чем преимущество использования UIButton над UIControl? Включение/отключение в моей реализации также меняет цвет и метку, а также я реализовал UIActivityControl...
Ну, мне кажется, вы пытаетесь создать кнопку (с некоторыми настраиваемыми функциями), так зачем подкласс UIControl? Опять же, ваши рассуждения не имеют смысла... Вы могли бы также переопределить enable/disable и поместить функцию didSet для изменения цвета/метки. Вы также можете добавить UIActivityControl в UIButton.
Это не по теме. Я не обязан оправдывать то, что здесь подклассифицировано, поскольку это не является частью вопроса. Вы говорите: «Ваши рассуждения не имеют смысла». Хорошо. У вас есть ответ на актуальный вопрос?
Я только что ответил на ваш вопрос в комментариях :)
Чтобы быть более ясным, UIButton имеет множество встроенных функций, которых нет у UIControl.





Вот рабочий код:
https://gist.github.com/alldne/22d340b36613ae5870b3472fa1c64654
Это мои рекомендации для вашего кода:
Размер представления, а именно вашей кнопки, определяется после того, как макет сделан. Что вам нужно сделать, так это просто установить правильный размер подслоев после макета. Поэтому я рекомендую вам установить размер и положение подслоев градиента в layoutSubviews.
override func layoutSubviews() {
super.layoutSubviews()
let center = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2)
selectedViewLayer.bounds = self.bounds
selectedViewLayer.position = center
gradientViewLayer.bounds = self.bounds
gradientViewLayer.position = center
}
Удалите shadowView и просто установите свойства слоя:
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3
layer.shadowOpacity = 0.3
layer.shadowColor = UIColor.black.cgColor
clipsToBounds = false
Если вам нужно использовать дополнительное представление для рисования тени, вы можете добавить представление один раз в init() и установить правильный размер и положение в layoutSubviews, или вы можете просто программно установить ограничения автоматического макета для суперпредставления.
После установки правильных размеров ваша анимация слоев градиента и представления контейнера не синхронизируется должным образом.
Кажется, что:
UIViewControllerTransitionCoordinator) имеет собственную продолжительность перехода и функцию плавности.UIView).CALayer без связанного UIView. Следовательно, он использует функцию синхронизации по умолчанию и продолжительность CoreAnimation.Чтобы синхронизировать анимацию, явно установите продолжительность анимации и функцию синхронизации, как показано ниже:
class ViewController: UIViewController {
...
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
CATransaction.setAnimationDuration(coordinator.transitionDuration)
CATransaction.setAnimationTimingFunction(coordinator.completionCurve.timingFunction)
}
...
}
// Swift 4
extension UIView.AnimationCurve {
var timingFunction: CAMediaTimingFunction {
let functionName: CAMediaTimingFunctionName
switch self {
case .easeIn:
functionName = kCAMediaTimingFunctionEaseIn as CAMediaTimingFunctionName
case .easeInOut:
functionName = kCAMediaTimingFunctionEaseInEaseOut as CAMediaTimingFunctionName
case .easeOut:
functionName = kCAMediaTimingFunctionEaseOut as CAMediaTimingFunctionName
case .linear:
functionName = kCAMediaTimingFunctionLinear as CAMediaTimingFunctionName
}
return CAMediaTimingFunction(name: functionName as String)
}
}
Ваши рассуждения о создании подкласса UIControl довольно странные.