I am completely lost. I have a toggle button(UISwitch) in one of my screens I have added a target to the switch to recognize changes in the switch. However when the switch is toggled nothing happens and I am confused and lost.
import Foundation
import UIKit
class PrivateCell: UITableViewCell {
var stackView: UIStackView?
let switchStatementLabel : UILabel = {
let switchStatementLabel = UILabel()
switchStatementLabel.textAlignment = .justified
switchStatementLabel.text = "Make Profile Private"
return switchStatementLabel
}()
let privateSwitch : UISwitch = {
let privateSwitch = UISwitch(frame: CGRect(x: 0, y: 0, width: 70, height: 70))
privateSwitch.isOn = false
privateSwitch.onTintColor = UIColor.rgb(red: 44, green: 152, blue: 229)
privateSwitch.addTarget(self, action: #selector(switchToggled(_:)), for: UIControlEvents.valueChanged)
return privateSwitch
}()
#objc func switchToggled(_ sender: UISwitch) {
if privateSwitch.isOn {
print("switch turned off")
}else{
print("switch turned on")
}
}
#objc func setupViews(){
backgroundColor = .white
stackView = UIStackView(arrangedSubviews: [ switchStatementLabel, privateSwitch])
stackView?.axis = .horizontal
stackView?.distribution = .equalSpacing
// stackView?.spacing = 10.0
if let firstStackView = stackView{
self.addSubview(firstStackView)
firstStackView.snp.makeConstraints { (make) in
make.edges.equalTo(self).inset(10)
}
}
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I have added my code could anyone help me please
Try this
import Foundation
import UIKit
class PrivateCell: UITableViewCell {
var stackView: UIStackView?
let switchStatementLabel : UILabel = {
let switchStatementLabel = UILabel()
switchStatementLabel.textAlignment = .justified
switchStatementLabel.text = "Make Profile Private"
return switchStatementLabel
}()
let privateSwitch : UISwitch = {
let privateSwitch = UISwitch(frame: CGRect(x: 0, y: 0, width: 70, height: 70))
privateSwitch.isOn = false
privateSwitch.onTintColor = UIColor.rgb(red: 44, green: 152, blue: 229)
return privateSwitch
}()
#objc func switchToggled(_ sender: UISwitch) {
if privateSwitch.isOn {
print("switch turned off")
}else{
print("switch turned on")
}
}
#objc func setupViews(){
privateSwitch.addTarget(self, action: #selector(switchToggled(_:)), for: UIControlEvents.valueChanged)
backgroundColor = .white
stackView = UIStackView(arrangedSubviews: [ switchStatementLabel, privateSwitch])
stackView?.axis = .horizontal
stackView?.distribution = .equalSpacing
// stackView?.spacing = 10.0
if let firstStackView = stackView{
self.addSubview(firstStackView)
firstStackView.snp.makeConstraints { (make) in
make.edges.equalTo(self).inset(10)
}
}
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
#IBOutlet var swtOnlineOfline: UISwitch!
swtOnlineOffline.addTarget(self, action: #selector(self.onlineOfflineSwitchValueChange(_:)), for: .valueChanged)
#IBAction func onlineOfflineSwitchValueChange(_ sender: UISwitch)
{
if sender.isOn
{
swtOnlineOffline.setOn(false, animated: true)
}
else
{
swtOnlineOffline.setOn(true, animated: true)
}
}
Related
version: Xcode 12.3
My project uses UISwitch heavily. Therefore, I am creating a custom Class to customize it.
I would like to set UISwitch default thumbTintColor and backgroundColor, and update these colors when the switch is togged on (isOn property).
I've found a solution but it does not work when I return to the viewController. The switch doesn't retain the setting:
subviews[0].subviews[0].backgroundColor = UIColor.white
I can't set isOn because it's read-only property.
Is there anyway that I can set the value change? I want something like below in the customSwitch Class:
Switch off: thumbTintColor = UIColor.yellow, backgroundColor =
UIcolor.black
Switch on: thumbTintColor = UIColor.red, backgroundColor =
UIcolor.white
below is my custom Class, can anyone help? thanks.
import Foundation
#IBDesignable
class CustomSwitch: UISwitch {
private var previousValue = false
private var returnPreviousValue = false
override var isOn: Bool {
return returnPreviousValue ? previousValue : super.isOn
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
previousValue = isOn
addTarget(self, action: #selector(_didChange), for: .valueChanged)
}
override init(frame: CGRect) {
super.init(frame: frame)
addTarget(self, action: #selector(_didChange), for: .valueChanged)
}
override func setOn(_ on: Bool, animated: Bool) {
super.setOn(on, animated: animated)
previousValue = on
}
#objc func _didChange() {
let isOn = self.isOn
if isOn == previousValue {
return
}
returnPreviousValue = true
willChangeValue(forKey: "on")
returnPreviousValue = false
previousValue = isOn
didChangeValue(forKey: "on")
}
}
Code
#IBDesignable
class CustomSwitch: UISwitch {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialSetUp()
}
convenience init() {
self.init(frame: .zero)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.initialSetUp()
}
private func initialSetUp() {
self.addTarget(self, action: #selector(refreshUI), for: .valueChanged)
self.refreshUI()
}
#objc private func refreshUI() {
/// Be aware that this is more of a hack than a solution
/// And can break in any upcoming release
let targetSubview = self.subviews.first?.subviews.first
if self.isOn {
self.thumbTintColor = .red
targetSubview?.backgroundColor = .white
}
else {
self.thumbTintColor = .yellow
targetSubview?.backgroundColor = .black
}
}
}
Usage
let customSwitch = CustomSwitch()
self.view.addSubview(customSwitch)
customSwitch.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customSwitch.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
customSwitch.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
])
I have a UITableViewController that contains a number of custom UITableViewCells types.
One of these types simply is a cell containing a UIStackView that itself contains one or more UIButton's.
When scrolling off screen and back on, the buttons are being added again. This happens on each scroll event.
Pre Scroll Image
Post Scroll Image
I understand that as a cell is reused for performance potentially what is happening is my setup code in cellForRowAt where I configure a cell is being executed again.
Hence it is adding 3 buttons from the data source, to the cell, which already contains buttons from the last render.
I do not understand how I can clear this up and prevent this behaviour however and would very much appreciated someone offering an insight as I am lost.
I have been able to prepare a small app that recreates this as I cannot share my current project as it is closed source.
I apologise for the mountain of code below, this is however the minimum required to simply drop into a project and recreate.
class TableViewController: UITableViewController {
let textCellId = "textCellId"
let buttonCellId = "buttonCellId"
// MARK: - Mock Data Source
let cellContent = [
Message(type: .buttonGroup, buttonGroup: [
MessageButton(label: "Button #1"),
MessageButton(label: "Button #2"),
MessageButton(label: "Button #3")
]),
Message(type: .text, text: "A"),
Message(type: .text, text: "B"),
Message(type: .text, text: "C"),
Message(type: .text, text: "D"),
Message(type: .text, text: "E"),
Message(type: .text, text: "F"),
Message(type: .text, text: "G"),
Message(type: .text, text: "H"),
Message(type: .text, text: "I"),
Message(type: .text, text: "J"),
Message(type: .text, text: "K"),
Message(type: .text, text: "L"),
Message(type: .text, text: "M"),
Message(type: .text, text: "N"),
Message(type: .text, text: "O"),
Message(type: .text, text: "P"),
Message(type: .text, text: "Q"),
Message(type: .text, text: "R"),
Message(type: .text, text: "S"),
]
override func viewDidLoad() {
super.viewDidLoad()
registerCells()
configureTableView()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellContent.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = cellContent[indexPath.row]
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: buttonCellId, for: indexPath) as! ButtonCell
cell.buttonGroupContent = item.buttonGroup
return cell
default:
let cell = tableView.dequeueReusableCell(withIdentifier: textCellId, for: indexPath) as! TextCell
cell.textLabel?.text = item.text
return cell
}
}
}
// MARK: - Misc TableView Setup
extension TableViewController {
fileprivate func registerCells() {
tableView.register(TextCell.self, forCellReuseIdentifier: textCellId)
tableView.register(ButtonCell.self, forCellReuseIdentifier: buttonCellId)
}
fileprivate func configureTableView() {
tableView.allowsSelection = false
tableView.alwaysBounceVertical = false
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 200
tableView.separatorStyle = .none
tableView.backgroundColor = UIColor.lightGray
tableView.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 50, right: 0)
tableView.tableFooterView = UIView()
}
}
// MARK: - Cell Types
class TextCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ButtonCell: UITableViewCell {
var buttonGroupContent: [MessageButton]? {
didSet {
anchorSubViews()
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate var button: UIButton {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.darkGray
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.layer.cornerRadius = 5
button.layer.masksToBounds = true
return button
}
fileprivate let buttonGroupStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
stackView.isLayoutMarginsRelativeArrangement = true
stackView.spacing = UIStackView.spacingUseSystem
return stackView
}()
}
extension ButtonCell {
fileprivate func anchorSubViews() {
guard let buttons = buttonGroupContent?.enumerated() else { return }
for (index, b) in buttons {
let btn = button
btn.setTitle(b.label, for: .normal)
btn.frame = CGRect(x: 0, y: 0, width: 200, height: 40)
btn.tag = index
buttonGroupStackView.addArrangedSubview(btn)
}
addSubview(buttonGroupStackView)
NSLayoutConstraint.activate([
buttonGroupStackView.topAnchor.constraint(equalTo: topAnchor),
buttonGroupStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
buttonGroupStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
buttonGroupStackView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
}
// MARK: - Misc for setup
struct MessageButton {
let label: String
}
enum MessageType {
case text, buttonGroup
}
struct Message {
let type: MessageType
let text: String?
let buttonGroup: [MessageButton]?
init(type: MessageType, text: String? = nil, buttonGroup: [MessageButton]? = nil) {
self.type = type
self.text = text
self.buttonGroup = buttonGroup
}
}
Because cells are reusable, content stays. So your old buttons are still in your stack view and you're adding next buttons every time.
To fix this, before you add new buttons to UIStackView remove old buttons
extension ButtonCell {
fileprivate func anchorSubViews() {
...
for case let button as UIButton in buttonGroupStackView.subviews {
button.removeFromSuperview()
}
for (index, b) in buttons {
...
buttonGroupStackView.addArrangedSubview(btn)
}
...
}
}
Make your button and buttonGroupStackView properties optional and weak. The addSubview method on will retain a strong reference to it's subview. So it never gets removed. And override prepareForReuse() to do whatever cleanup necessary and make sure the stackview has been removed from the cell. Here's how you could do it:
class ButtonCell: UITableViewCell {
var buttonGroupContent: [MessageButton]? {
didSet {
anchorSubViews()
}
}
fileprivate weak var buttonGroupStackView: UIStackView?
// MARK: - Initialization
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.initialSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialSetup()
}
private func initialSetup() -> Void {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
stackView.isLayoutMarginsRelativeArrangement = true
stackView.spacing = UIStackView.spacingUseSystem
self.addSubview(stackView)
self.buttonGroupStackView = stackView
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
// MARK: - Subclass Overrides
override func prepareForReuse() {
super.prepareForReuse()
self.buttonGroupStackView?.subviews.forEach({ $0.removeFromSuperview()} )
}
// MARK: - Private
fileprivate func createButton() -> UIButton {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.darkGray
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.layer.cornerRadius = 5
button.layer.masksToBounds = true
return button
}
}
extension ButtonCell {
fileprivate func anchorSubViews() {
guard let buttons = buttonGroupContent?.enumerated() else { return }
for (index, b) in buttons {
let btn = self.createButton()
btn.setTitle(b.label, for: .normal)
btn.frame = CGRect(x: 0, y: 0, width: 200, height: 40)
btn.tag = index
self.buttonGroupStackView?.addArrangedSubview(btn)
}
}
}
It is always recommended to use a weak reference to subviews or IBOutlet properties unless you require otherwise.
i have created CustomeView that contain Scroll-view.inside scroll view there is one container view that contain image view plus two button(Okay and cancel).
Following is my view-hierarchy.
CustomeView -> ScrollView -> ContainerView -> (imageView + OtherComponent).
There are two problem i faced.
while zoom in-out imageview,CustomeView is also zoomed in-out with
respect to Scrollview.
other component postion is changed while zoom in-out.
class cameraPreview : UIView , UIScrollViewDelegate {
var selectedImage : UIImage!
var backGroundView = UIView()
var imageScrollview = UIScrollView()
var metaData : [String:Any]?
let backgroundImageView = UIImageView()
var closeButton : UIButton = {
let button = UIButton(type: UIButtonType.custom)
button.setImage(UIImage(named:"closeWhite"), for: .normal)
button.addTarget(self, action: #selector(closeClick), for: .touchUpInside)
return button
}()
var okButton : UIButton = {
let button = UIButton()
button.setTitle("OK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(okClick), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commoninit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commoninit()
}
required init(image:UIImage,frame:CGRect,metaData:[String:Any]?) {
super.init(frame: frame)
self.selectedImage = image
self.metaData = metaData
commoninit()
}
override func layoutSubviews() {
}
func commoninit() {
backGroundView.backgroundColor = UIColor.white
backgroundImageView.contentMode = .scaleAspectFit
self.addSubview(imageScrollview)
imageScrollview.addSubview(backGroundView)
backGroundView.addSubview(backgroundImageView)
backGroundView.addSubview(closeButton)
backGroundView.addSubview(okButton)
imageScrollview.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
backGroundView.snp.makeConstraints { (make) in
make.edges.equalTo(imageScrollview)
make.height.width.equalTo(self)
}
backgroundImageView.snp.makeConstraints { (make) in
make.edges.equalTo(backGroundView)
}
okButton.snp.makeConstraints { (make) in
make.width.equalTo(80)
make.height.equalTo(30)
make.centerX.equalTo(backGroundView.snp.centerX)
make.bottom.equalTo(backGroundView).offset(-20)
}
closeButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.left.equalTo(20)
make.top.equalTo(10)
}
backgroundImageView.image = selectedImage
imageScrollview.delegate = self
imageScrollview.minimumZoomScale = 1.0
imageScrollview.maximumZoomScale = 6.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return backgroundImageView
}
#objc func closeClick(sender:UIButton) {
self.removeFromSuperview()
}
#objc func okClick(sender:UIButton) {
if let topCotroller = UIApplication.shared.gettopMostViewController() {
self.removeFromSuperview()
let mediaDetailController = UploadDetailsViewController.instantiate(fromAppStoryboard: .Upload)
mediaDetailController.mediaImage = selectedImage
if metaData != nil {
mediaDetailController.exifDictionary = metaData![kCGImagePropertyExifDictionary as String] as? [String : AnyObject]
}
topCotroller.navigationController?.pushViewController(mediaDetailController, animated: true)
}
}
}
Following is code to add cameraPreview inside current ControllerView
let imagePreview = cameraPreview(image: image, frame: UIScreen.main.bounds,metaData:metaData)
self.view.addSubview(imagePreview)
your other component is zoom in-out because you put those component inside ScrollView.if you simply put those component out side of ScrollView than your component will not zoom in-out with respect to ScrollView.
Following is Source Code.
class cameraPreview : UIView , UIScrollViewDelegate {
var selectedImage : UIImage!
var backGroundView = UIView()
var imageScrollview = UIScrollView()
var metaData : [String:Any]?
let backgroundImageView = UIImageView()
var closeButton : UIButton = {
let button = UIButton(type: UIButtonType.custom)
button.setImage(UIImage(named:"closeWhite"), for: .normal)
button.addTarget(self, action: #selector(closeClick), for: .touchUpInside)
return button
}()
var okButton : UIButton = {
let button = UIButton()
button.setTitle("OK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(okClick), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commoninit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commoninit()
}
required init(image:UIImage,frame:CGRect,metaData:[String:Any]?) {
super.init(frame: frame)
self.selectedImage = image
self.metaData = metaData
commoninit()
}
override func layoutSubviews() {
}
func commoninit() {
self.backgroundColor = UIColor.white
backGroundView.backgroundColor = UIColor.clear
backgroundImageView.contentMode = .scaleAspectFit
self.addSubview(imageScrollview)
imageScrollview.addSubview(backGroundView)
backGroundView.addSubview(backgroundImageView)
self.addSubview(closeButton)
self.addSubview(okButton)
imageScrollview.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
backGroundView.snp.makeConstraints { (make) in
make.edges.equalTo(imageScrollview)
make.height.width.equalTo(self)
}
backgroundImageView.snp.makeConstraints { (make) in
make.edges.equalTo(backGroundView)
}
okButton.snp.makeConstraints { (make) in
make.width.equalTo(80)
make.height.equalTo(30)
make.centerX.equalTo(self)
make.bottom.equalTo(self).offset(-20)
}
closeButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.left.equalTo(20)
make.top.equalTo(10)
}
backgroundImageView.image = selectedImage
imageScrollview.delegate = self
imageScrollview.minimumZoomScale = 1.0
imageScrollview.maximumZoomScale = 6.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return backgroundImageView
}
#objc func closeClick(sender:UIButton) {
self.removeFromSuperview()
}
#objc func okClick(sender:UIButton) {
if let topCotroller = UIApplication.shared.gettopMostViewController() {
self.removeFromSuperview()
let mediaDetailController = UploadDetailsViewController.instantiate(fromAppStoryboard: .Upload)
mediaDetailController.mediaImage = selectedImage
if metaData != nil {
mediaDetailController.exifDictionary = metaData![kCGImagePropertyExifDictionary as String] as? [String : AnyObject]
}
topCotroller.navigationController?.pushViewController(mediaDetailController, animated: true)
}
}
}
I am trying to implement the animation of 2 views using SnapKit.
Here is my Animation view:
class MatchAnimation: UIView {
let viewBackground: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.75)
view.alpha = 1
return view
}()
let matchView: UIView = {
let view = UIView()
return view
}()
let matchLabel: UILabel = {
let label = UILabel()
label.text = "Title"
label.textColor = .white
label.textAlignment = .center
return label
}()
let leftAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 91/2
return view
}()
let rightAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.layer.cornerRadius = 91/2
return view
}()
let goToChatButton: UIButton = {
let button = UIButton()
button.setTitle("Button", for: .normal)
button.backgroundColor = .red
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 24.5
return button
}()
init() {
super.init(frame: UIScreen.main.bounds)
viewBackground.frame = self.frame
self.addSubview(viewBackground)
self.addSubview(matchView)
matchView.addSubview(matchLabel)
matchView.addSubview(leftAvatarBg)
matchView.addSubview(rightAvatarBg)
matchView.addSubview(goToChatButton)
matchView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.center.equalToSuperview()
}
matchLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 193, height: 40))
}
leftAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(matchLabel.snp.bottom).offset(20)
make.size.equalTo(CGSize(width: 91, height: 91))
make.right.equalTo(self.snp.left).offset(0)
}
rightAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(leftAvatarBg)
make.size.equalTo(leftAvatarBg)
make.left.equalTo(self.snp.right).inset(0)
}
goToChatButton.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 171, height: 50))
make.top.equalTo(leftAvatarBg.snp.bottom).offset(25)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
}
}
func animate() {
UIView.animate(withDuration: 5) {
self.leftAvatarBg.snp.updateConstraints { (make) in
make.right.equalTo(self.snp.left).offset(UIScreen.main.bounds.width/2+30)
}
self.rightAvatarBg.snp.updateConstraints { (make) in
make.left.equalTo(self.snp.right).inset(UIScreen.main.bounds.width/2+30)
}
self.layoutIfNeeded()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The 2 views I tried to animate are leftAvatarBg and rightAvatarBg.
Before the animation, I set them at the exterior of the screen and want to make them slide from left to right for one view and from right to left for the other.
In my controller, I just call:
func setupAnimation() {
let matchView = MatchAnimation()
view.addSubview(matchView)
matchView.animate()
}
The result of this is that the entire view is animating (scaling).
Did I miss something?
UPDATE: Thanks to swift2geek, it seems that they is a conflict between the creation of the object and the animation. In his solution, he trigger the animation by pressing a button. In my case, I want to trigger the animation as soon as possible and automatically. How can I ensure that the animation will be fired after the creation of the object ?
im not good with your bdsm SnapKit, so put the right constraints on your own. So the main reason its not working - you should separate animation and object creation.
your viewcontroller:
import UIKit
class ViewController: UIViewController {
#IBOutlet var matchButton: MatchButton!
#IBOutlet var matchView: MatchAnimation!
override func viewDidLoad() {
super.viewDidLoad()
setupAnimation()
setupButton()
}
func setupAnimation() {
matchView = MatchAnimation()
matchView.isUserInteractionEnabled = true
view.addSubview(matchView)
}
func setupButton() {
matchButton = MatchButton()
matchButton.isUserInteractionEnabled = true
matchButton.isEnabled = true
matchButton.addTarget(self, action: #selector(pressed(_:)), for: .touchUpInside)
matchView.addSubview(matchButton)
}
#objc func pressed(_ sender: MatchButton!) {
print("button tapped")
matchView.animate()
}
}
your MatchAnimation class:
import UIKit
import SnapKit
class MatchAnimation: UIView {
let viewBackground: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.75)
view.alpha = 1
return view
}()
let matchView: UIView = {
let view = UIView()
return view
}()
let matchLabel: UILabel = {
let label = UILabel()
label.text = "Title"
label.textColor = .white
label.textAlignment = .center
return label
}()
let leftAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 91/2
return view
}()
let rightAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.layer.cornerRadius = 91/2
return view
}()
init() {
super.init(frame: UIScreen.main.bounds)
viewBackground.frame = self.frame
self.addSubview(viewBackground)
self.addSubview(matchView)
matchView.addSubview(matchLabel)
matchView.addSubview(leftAvatarBg)
matchView.addSubview(rightAvatarBg)
matchView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.center.equalToSuperview()
}
matchLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 193, height: 40))
}
leftAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(matchLabel.snp.bottom).offset(20)
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 91, height: 91))
make.right.equalTo(self.snp.left).offset(90)
}
rightAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(leftAvatarBg)
make.size.equalTo(leftAvatarBg)
make.left.equalTo(self.snp.right).inset(120)
}
}
func animate() {
UIView.animate(withDuration: 5) {
self.leftAvatarBg.snp.updateConstraints { (make) in
make.right.equalTo(self.snp.left).offset(UIScreen.main.bounds.width/2+30)
}
self.rightAvatarBg.snp.updateConstraints { (make) in
make.left.equalTo(self.snp.right).inset(UIScreen.main.bounds.width/2+30)
}
self.layoutIfNeeded()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and MatchButton:
import UIKit
import SnapKit
class MatchButton: UIButton {
let goToChatButton: UIButton = {
let button = UIButton()
button.setTitle("Button", for: .normal)
button.backgroundColor = .red
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 24.5
return button
}()
init() {
super.init(frame: UIScreen.main.bounds)
self.addSubview(goToChatButton)
goToChatButton.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 171, height: 50))
make.top.greaterThanOrEqualTo(100)
make.centerX.equalToSuperview()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If you want your animation to fire as soon as possible, just add layout animate() function to viewDidAppear.
I customized a UISearchbar, the code is like this:
class CustomSearchBar: UIView {
lazy var searchBar: MySearchBar = {
let search = MySearchBar()
search.tintColor = UIColor.orange
search.barTintColor = UIColor.yellow
search.searchBarStyle = .minimal
search.searchTextPositionAdjustment = UIOffset(horizontal: 10, vertical: 0)
return search
}()
lazy var cancelButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("取消", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
addSubview(searchBar)
addSubview(cancelButton)
}
private func setupConstraints() {
searchBar.snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(20)
make.top.equalToSuperview().offset(6)
make.height.equalTo(32)
make.width.equalToSuperview().offset(-80)
}
cancelButton.snp.makeConstraints { (make) in
make.left.equalTo(searchBar.snp.right).offset(13)
make.centerY.equalTo(searchBar)
}
}
}
class MySearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func indexOfSearchFieldInSubviews() -> Int! {
var index: Int!
let searchBarView = subviews[0]
for (i, view) in searchBarView.subviews.enumerated() {
if view.isKind(of: UITextField.self) {
index = i
break
}
}
return index
}
override func draw(_ rect: CGRect) {
if let index = indexOfSearchFieldInSubviews() {
let searchField: UITextField = subviews[0].subviews[index] as! UITextField
searchField.leftView = UIImageView(image: UIImage(named: "Search_icon"))
searchField.font = UIFont.systemFont(ofSize: 14)
searchField.borderStyle = .none
searchField.layer.masksToBounds = true
searchField.layer.cornerRadius = 4
searchField.backgroundColor = barTintColor
searchField.contentVerticalAlignment = .center
searchField.placeholder = "input something"
}
}
}
we can see that it is very simple. Just added a searchBar and cancelButton.
Then I added it to navigationBar as subView, make it becomeFirstResponder in viewController.
class ViewController: UIViewController {
lazy var customeSearchBar = CustomSearchBar()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.addSubview(customeSearchBar)
customeSearchBar.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.centerY.equalToSuperview()
make.height.equalTo(44)
}
customeSearchBar.searchBar.becomeFirstResponder()
}
}
At first, I input content is ok.
When I continue input content, then the issue shows up:
It looks like the content move to the bottom. I tried to find something to fix the problem. But I didn't get it.
It is a strange thing that works fine in simulate and some iPhone. But one of my phone works wrong.