matchmakerViewController:didFindMatch is not being called after accepting Invite - ios

I am making a real time Game Center game, with GameKit as a new programmer, however I have run into a few road blocks.
I am able to properly initialize the local player, present the matchmaking viewController, and receive and accept invites, however, even after I accept the invite matchmakerViewController:didFindMatch is not called and no match is returned to me. There is a high likelihood that I did not accept the invitation correct, but according to apples developer forums I have. If someone could review this code and let me know how to have that method be called that'd be great!
let request = GKMatchRequest()
class SecondScreenViewController : UIViewController, UINavigationControllerDelegate, GKMatchmakerViewControllerDelegate, GKLocalPlayerListener {
let button = UIButton()
let softRed = UIColor(red: 1, green: 61 / 255, blue: 61 / 255, alpha: 1)
let softblue = UIColor(red: 38 / 255, green: 149 / 255, blue: 1, alpha: 1)
let TestMatchMaker = GKMatchmaker()
let TestMatch = GKMatch()
var TestViewController = GKMatchmakerViewController(matchRequest: request)
var player = GKLocalPlayer.local
override func viewDidLoad() {
super.viewDidLoad()
player.register(self)
setupButton()
SetupRequest()
view.backgroundColor = softblue
}
func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
dismiss(animated: true, completion: nil)
print("cancelled")
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {
print(error.localizedDescription + "THIS IS AN ERROR FROM THE VIEWCONTROLLER")
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
print("Found Match Successfully")
let TestMatch = match
print(TestMatch)
}
func player(_ player: GKPlayer, didAccept invite: GKInvite) {
print(invite)
print("Aceppted Invite")
TestViewController?.setHostedPlayer(player, didConnect: true)
}
func player(_ player: GKPlayer, didRequestMatchWithRecipients recipientPlayers: [GKPlayer]) {
print("Requested Match")
}
func SetupRequest() {
request.minPlayers = 2
request.maxPlayers = 2
request.inviteMessage = "test Invite"
}
func setupButton() {
button.backgroundColor = UIColor.white
button.setTitleColor(softblue, for: .normal)
button.setTitle("MatchMake", for: .normal)
button.addTarget(self, action: #selector(ButtonTapped), for: .touchUpInside)
view.addSubview(button)
setupButtonConstraints()
}
func setupButtonConstraints() {
button.translatesAutoresizingMaskIntoConstraints = false
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
button.heightAnchor.constraint(equalToConstant: 100).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
}
#objc func ButtonTapped() {
present(TestViewController!, animated: true, completion: {
self.TestViewController!.delegate = self})
TestViewController?.matchmakerDelegate = self
}
}

The only thing I see that might be causing it is when you set the matchmaker delegate. Here is the code I am using to invite players and it is working properly:
func invitePlayers(){
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 4
request.defaultNumberOfPlayers = 2
let vc = GKMatchmakerViewController(matchRequest: request)
vc?.matchmakerDelegate = self
self.present(vc!, animated: true)
}
notice that the matchmakerDelegate is set before the ViewController is presented.

Related

Detect if volume button is pressed when app is running in the background - Swift iOS

Goal and Question: My goal is to have my application launch when the user presses the volume up button three times. Is it possible to enable this functionality and if so, how?
I've realized that when using the "Audio, AirPlay, and Picture in Picture" background mode, it's possible to register that the volume button was tapped, even when the display is off and the app is running in the background. However, this only works if audio is playing from the app. When audio isn't playing, pressing the volume button isn't registered when the app is in the background.
Audio boxed checked in Background mode
I used this code to test out this feature, courtesy of https://github.com/rebeloper/BackgroundModesSwift/tree/master/BackgroundModesSwift
Here's the audio view controller, I slightly modified it from the original code on Github to include the listenVolumeButton(), viewWillAppear(), and the first if statement in observeValue()
import TinyConstraints
import AVFoundation
class AudioViewController: UIViewController {
var counter = 1
lazy var player: AVQueuePlayer = self.makePlayer()
var outputVolumeObserve: NSKeyValueObservation?
let audioSession = AVAudioSession.sharedInstance()
private lazy var songs: [AVPlayerItem] = {
let songNames = ["Getting it Done"]
return songNames.map {
let url = Bundle.main.url(forResource: $0, withExtension: "mp3")!
return AVPlayerItem(url: url)
}
}()
lazy var songLabel: UILabel = {
var label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 34)
label.textColor = #colorLiteral(red: 0.1764705882, green: 0.2039215686, blue: 0.2117647059, alpha: 1)
return label
}()
lazy var timeLabel: UILabel = {
var label = UILabel()
label.font = UIFont.systemFont(ofSize: 28)
label.textColor = #colorLiteral(red: 0.1764705882, green: 0.2039215686, blue: 0.2117647059, alpha: 1)
return label
}()
lazy var playPauseButton: UIButton = {
var button = UIButton()
button.setTitle(button.isSelected ? "Pause" : "Play", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
button.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
button.backgroundColor = #colorLiteral(red: 0.03529411765, green: 0.5176470588, blue: 0.8901960784, alpha: 1)
button.layer.cornerRadius = 8
button.layer.masksToBounds = true
button.addTarget(self, action: #selector(playPauseAction(_:)), for: .touchUpInside)
return button
}()
#objc func playPauseAction(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
player.play()
} else {
player.pause()
}
playPauseButton.setTitle(playPauseButton.isSelected ? "Pause" : "Play", for: .normal)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setupViews()
addPeriodicTimeObserver()
}
func setupViews() {
view.backgroundColor = #colorLiteral(red: 0.9254901961, green: 0.9411764706, blue: 0.9450980392, alpha: 1)
view.addSubview(songLabel)
view.addSubview(timeLabel)
view.addSubview(playPauseButton)
songLabel.centerInSuperview(offset: CGPoint(x: 0, y: -20))
timeLabel.centerInSuperview(offset: CGPoint(x: 0, y: 30))
playPauseButton.edgesToSuperview(excluding: .top, insets: .bottom(10) + .right(10) + .left(10), usingSafeArea: true)
playPauseButton.height(50)
}
func addPeriodicTimeObserver() {
do {
try AVAudioSession.sharedInstance().setCategory(
AVAudioSession.Category.playAndRecord,
mode: .default,
options: [])
} catch {
print("Failed to set audio session category. Error: \(error)")
}
player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 100), queue: DispatchQueue.main) { [weak self] time in
guard let self = self else { return }
let timeString = String(format: "%02.2f", CMTimeGetSeconds(time))
// TODO: check applicationState
}
}
private func makePlayer() -> AVQueuePlayer {
let player = AVQueuePlayer(items: songs)
player.actionAtItemEnd = .advance
player.addObserver(self, forKeyPath: "currentItem", options: [.new, .initial] , context: nil)
return player
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume" {
counter += 1
print(counter)
}
if keyPath == "currentItem",
let player = object as? AVPlayer,
let currentItem = player.currentItem?.asset as? AVURLAsset {
songLabel.text = currentItem.url.lastPathComponent
}
}
func listenVolumeButton() {
do {
try audioSession.setActive(true)
} catch {
print("some error")
}
audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
}
override func viewWillAppear(_ animated: Bool) {
listenVolumeButton()
}
}
Additionally, I was wondering if there is any way to programmatically use the shortcuts app that's preinstalled on iPhones to achieve this functionality, SwiftUI Widgets, or creating a notification that the operating system sends to the app when the volume button is pressed.
Any help is appreciated, Thank You in advance.

iOS Swift protocol & delegate

Why my delegation is not working?
With my sample the action button works when clicked, but for some reason it does not reach the didAction function in my second controller.
protocol HomeControllerDelegate: class {
func didAction()
}
class HomeController: UIViewController {
weak var delegate: HomeControllerDelegate?
private let actionButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Action", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.setHeight(50)
button.setWidth(100)
button.addTarget(self, action: #selector(handleAction), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(actionButton)
actionButton.centerX(inView: view)
actionButton.centerY(inView: view)
}
#objc func handleAction() {
print("DEBUG: Handle Action Button delegate here....")
delegate?.didAction()
}
}
class SecondController: UIViewController {
let homeController = HomeController()
override func viewDidLoad() {
super.viewDidLoad()
homeController.delegate = self
}
}
extension SecondController: HomeControllerDelegate {
func didAction() {
print("DEBUG: In SecondController - didAction()")
}
}
Many thanks for the guidance, it has made me look at the fundamentals and also what to research.
I have created the below which solves my problem.
A container controller that Instantiates two ViewControlers and sets them as childVC's
Then created a protocol on FirstChildVC and SecondChildVC is the delegate
All is working now an I understand a lot better.
class ContainerController: UIViewController {
let secondChildVC = SecondChildVC()
let firstChildVC = FirstChildVC()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
addFirstChildVC()
addSecondChildVC()
}
func addSecondChildVC(){
addChild(secondChildVC)
view.addSubview(secondChildVC.view)
secondChildVC.didMove(toParent: self)
setSecondChildVCConstraints()
}
func addFirstChildVC(){
addChild(firstChildVC)
view.addSubview(firstChildVC.view)
firstChildVC.delegateActionButton = secondChildVC
firstChildVC.didMove(toParent: self)
setFirstChildVCConstraints()
}
func setFirstChildVCConstraints() {
firstChildVC.view.translatesAutoresizingMaskIntoConstraints = false
firstChildVC.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
firstChildVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
firstChildVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
firstChildVC.view.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
func setSecondChildVCConstraints() {
secondChildVC.view.translatesAutoresizingMaskIntoConstraints = false
secondChildVC.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
secondChildVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
secondChildVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
secondChildVC.view.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
}
protocol FirstChildVCDelegate: class {
func didAction(data: String)
}
class FirstChildVC: UIViewController {
weak var delegateActionButton: FirstChildVCDelegate!
private let actionButton: UIButton = {
let button = UIButton(type: .system)
let buttonHeight = 30
button.setTitle("Button", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.layer.cornerRadius = CGFloat(buttonHeight / 2)
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: CGFloat(buttonHeight)).isActive = true
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.addTarget(self, action: #selector(handleAction), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
configureActionButtonView()
}
func configureActionButtonView() {
view.addSubview(actionButton)
actionButton.translatesAutoresizingMaskIntoConstraints = false
actionButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
actionButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
#objc func handleAction() {
delegateActionButton.didAction(data: "Button Pressed")
}
}
class SecondChildVC: UIViewController {
private let actionButtonLabel: UILabel = {
let label = UILabel()
label.text = "test"
label.font = .systemFont(ofSize: 18)
label.textColor = .white
label.isHidden = true
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPurple
configureActionButtonLabel()
}
func configureActionButtonLabel() {
view.addSubview(actionButtonLabel)
actionButtonLabel.translatesAutoresizingMaskIntoConstraints = false
actionButtonLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
actionButtonLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
extension SecondChildVC: FirstChildVCDelegate {
func didAction(data: String) {
actionButtonLabel.isHidden.toggle()
actionButtonLabel.text = data
}
}
Initial State
State once Button pressed

Show UIView When Popping Back on a Navigation Controller

So I'm trying to show an UIView in a stack view when I click continue on a controller and pop back to the previous controller. However, I'm having trouble showing the view when I pop. It will show if I present the controller, but I need it to show when I pop. How should I go about this? Thank you.
// ServiceDetailController
// MARK: - Properties
lazy var dateContainer: ShadowCardView = {
let view = ShadowCardView()
view.backgroundColor = .white
view.addShadow()
view.setHeight(height: 40)
let stack = UIStackView(arrangedSubviews: [calendarIcon, dateLabel, timeOfDayLabel])
stack.axis = .horizontal
stack.distribution = .fillProportionally
stack.spacing = 8
view.addSubview(stack)
stack.centerY(inView: view)
stack.anchor(left: view.leftAnchor, right: view.rightAnchor, paddingLeft: 12, paddingRight: 70)
view.addSubview(closeButton)
closeButton.centerY(inView: view)
closeButton.anchor(right: view.rightAnchor, paddingRight: 12)
return view
}()
lazy var dateStack = UIStackView(arrangedSubviews: [dateLabelStack, dateContainer, dateContainerView])
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
// MARK: - Selectors
#objc func handleDateCreationTapped() {
let controller = DateCreationController()
controller.jobService = self.jobService
navigationController?.pushViewController(controller, animated: true)
}
// MARK: - Helper Functions
fileprivate func configureUI() {
setupNavigationBar()
view.backgroundColor = .groupTableViewBackground
setupServiceInfoView()
setupFormView()
}
fileprivate func setupFormView() {
showDataContainer(shouldShow: false)
setupTapGestureRecognizers()
}
fileprivate func setupTapGestureRecognizers() {
let dateTap = UITapGestureRecognizer(target: self, action: #selector(handleDateCreationTapped))
dateTap.numberOfTapsRequired = 1
dateTextField.addGestureRecognizer(dateTap)
}
func showDateContainer(shouldShow: Bool) {
if shouldShow {
dateContainer.isHidden = false
dateStack.spacing = 5
} else {
dateContainer.isHidden = true
dateStack.spacing = -18
}
}
// DateCreationController
// MARK: - Properties
private let continueButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Continue", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
button.backgroundColor = .darkGray
button.setHeight(height: 50)
button.layer.cornerRadius = 8
button.addTarget(self, action: #selector(handleContinue), for: .touchUpInside)
return button
}()
// MARK: - Selectors
#objc func handleContinue() {
let controller = ServiceDetailController()
controller.showDateContainer(shouldShow: true)
navigationController?.popViewController(animated: true)
}
The main problem is this func in your DateCreationController:
#objc func handleContinue() {
// here, you are creating a NEW instance of ServiceDetailController
let controller = ServiceDetailController()
controller.showDateContainer(shouldShow: true)
// here, you are popping back to where you came from... the EXISTING instance of ServiceDetailController
navigationController?.popViewController(animated: true)
}
You need to use either delegate/protocol pattern or a closure.
Here's an example using a closure...
Add this var to your DateCreationController class:
var continueCallback: ((Bool)->())?
In your ServiceDetailController class, when you instantiate and push your DateCreationController, you'll also setup that closure:
#objc func handleDateCreationTapped() {
let controller = DateCreationController()
controller.jobService = self.jobService
// add the closure
controller.continueCallback = { [weak self] shouldShow in
guard let self = self else { return }
self.showDateContainer(shouldShow: shouldShow)
self.navigationController?.popViewController(animated: true)
}
navigationController?.pushViewController(controller, animated: true)
}
Then, in your button action in DateCreationController, you "call back" using that closure:
#objc func handleContinue() {
continueCallback?(true)
}
Here's a runnable example. It creates a simple yellow view as the dateContainer view, but it is hidden on load. Tapping anywhere will push to DateCreationController, which has a single "Continue" button. Tapping that button will execute the closure, where the dateContainer view will be set to visible and we'll pop back:
class ServiceDetailController: UIViewController {
var jobService: String = "abc"
// plain yellow view
let dateContainer: UIView = {
let view = UIView()
view.backgroundColor = .yellow
return view
}()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
let infoLabel = UILabel()
infoLabel.text = "Tap anywhere"
view.addSubview(infoLabel)
view.addSubview(dateContainer)
infoLabel.translatesAutoresizingMaskIntoConstraints = false
dateContainer.translatesAutoresizingMaskIntoConstraints = false
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
infoLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
infoLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
dateContainer.topAnchor.constraint(equalTo: infoLabel.bottomAnchor, constant: 20.0),
dateContainer.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
dateContainer.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
dateContainer.heightAnchor.constraint(equalToConstant: 100.0),
])
setupTapGestureRecognizers()
// hide dateContainer on load
showDateContainer(shouldShow: false)
}
// MARK: - Selectors
#objc func handleDateCreationTapped() {
let controller = DateCreationController()
controller.jobService = self.jobService
// add the closure
controller.continueCallback = { [weak self] shouldShow in
guard let self = self else { return }
self.showDateContainer(shouldShow: shouldShow)
self.navigationController?.popViewController(animated: true)
}
navigationController?.pushViewController(controller, animated: true)
}
// MARK: - Helper Functions
fileprivate func setupTapGestureRecognizers() {
let dateTap = UITapGestureRecognizer(target: self, action: #selector(handleDateCreationTapped))
dateTap.numberOfTapsRequired = 1
view.addGestureRecognizer(dateTap)
}
func showDateContainer(shouldShow: Bool) {
if shouldShow {
dateContainer.isHidden = false
} else {
dateContainer.isHidden = true
}
}
}
class DateCreationController: UIViewController {
var jobService: String = "abc"
var continueCallback: ((Bool)->())?
lazy var continueButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Continue", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
button.backgroundColor = .darkGray
button.layer.cornerRadius = 8
button.addTarget(self, action: #selector(handleContinue), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
view.addSubview(continueButton)
continueButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
continueButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
continueButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
continueButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75),
continueButton.heightAnchor.constraint(equalToConstant: 50.0),
])
}
// MARK: - Selectors
#objc func handleContinue() {
continueCallback?(true)
}
}

Protocol and delegate pattern not calling the method

Implementing a protocol/delegate pattern as suggested by expert #DonMag, i am unable to get the code working...
I show a ratings window like below and when the user changes the ratings , i want the ratings emoji to update and the controller to pop, i also use animation, now for view i have a separate ratings class and for controller a separate class,
The problem is this function gets tapped , i can detect it but it does not reach the protocol method
#objc func ratingButtonTapped(_ sender: UIButton) {
// print(sender.titleLabel?.text)
guard let t = sender.titleLabel?.text else {
return
}
ratingDelegate?.defineTheratings(t)
}
Controller class
import UIKit
import CoreData
protocol RatingsPresentation: class {
func defineTheratings(_ ratings: String)
}
class RatingsViewController: UIViewController, RatingsPresentation {
func defineTheratings(_ ratings: String) {
print("ratings seleced\(ratings)")
self.restaurant.rating = ratings
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
appDelegate.saveContext()
}
if self.presentedViewController != nil {
self.dismiss(animated: true, completion: nil)
}
else {
self.navigationController?.popViewController(animated: true)
}
}
var restaurant: Restaurant!
var rates = RatingsView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(rates.view)
//Delegates
let vc = RatingsView()
// set the rating delegate
vc.ratingDelegate = self
//
if let restaurantImage = restaurant.image {
rates.bkgImageView.image = UIImage(data: restaurantImage as Data)
}
rates.crossBtn.addTarget(self, action: #selector(closeRatings), for: .touchUpInside)
let animateCross = CGAffineTransform(translationX: 1000, y: 0)
rates.crossBtn.transform = animateCross
}
#objc func closeRatings() {
navigationController?.popViewController(animated: true)
}
}
View Class
import UIKit
class RatingsView: UIViewController {
var bkgImageView = UIImageView()
var crossBtn = UIButton()
var btnCollection: [UIButton] = []
weak var ratingDelegate: RatingsPresentation?
override func viewDidLoad() {
super.viewDidLoad()
let ratings: [String] = ["cool", "happy","love", "sad", "angry"]
let animationBlur = UIBlurEffect(style: .dark)
let visualBlurView = UIVisualEffectView(effect: animationBlur)
// add elements to self
view.addSubview(bkgImageView)
view.addSubview(visualBlurView)
bkgImageView.translatesAutoresizingMaskIntoConstraints = false
visualBlurView.translatesAutoresizingMaskIntoConstraints = false
bkgImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
bkgImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
bkgImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
bkgImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// constrain visualBlurView to all 4 sides
visualBlurView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
visualBlurView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
visualBlurView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
visualBlurView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
bkgImageView.contentMode = .scaleAspectFill
// Do any additional setup after loading the view.
//Add stackView
let stackEmoji = UIStackView()
stackEmoji.translatesAutoresizingMaskIntoConstraints = false
stackEmoji.axis = .vertical
stackEmoji.spacing = 5
stackEmoji.distribution = .fill
stackEmoji.alignment = .top
let font = UIFont(name: "Rubik-Medium", size: 28)
let fontM = UIFontMetrics(forTextStyle: .body)
ratings.forEach { (btn) in
let b = UIButton()
b.setTitle(btn, for: .normal)
b.titleLabel?.font = fontM.scaledFont(for: font!)
b.setImage(UIImage(named: btn), for: .normal)
stackEmoji.addArrangedSubview(b)
btnCollection.append(b)
//btn animation
let sizeAnimation = CGAffineTransform(scaleX: 5, y: 5)
let positionAnimation = CGAffineTransform(translationX: 1000, y: 0)
let combinedAninaton = sizeAnimation.concatenating(positionAnimation)
b.transform = combinedAninaton
b.addTarget(self, action: #selector(ratingButtonTapped(_:)), for: .touchUpInside)
}
view.addSubview(stackEmoji)
stackEmoji.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackEmoji.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
// if let img = UIImage(named: "cross") {
// crossBtn.setImage(img, for: [])
// }
crossBtn.setTitle("X", for: [])
crossBtn.setTitleColor(.white, for: .normal)
crossBtn.setTitleColor(.gray, for: .highlighted)
crossBtn.titleLabel?.font = UIFont.systemFont(ofSize: 44, weight: .bold)
view.addSubview(crossBtn)
crossBtn.translatesAutoresizingMaskIntoConstraints = false
crossBtn.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
crossBtn.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
}
#objc func ratingButtonTapped(_ sender: UIButton) {
// print(sender.titleLabel?.text)
guard let t = sender.titleLabel?.text else {
return
}
ratingDelegate?.defineTheratings(t)
}
}
Problem is here
var rates = RatingsView() //// shown 1
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(rates.view) /////// you add this as a subview
//Delegates
let vc = RatingsView() //////// this is useless - not shown 1
// set the rating delegate
vc.ratingDelegate = self ////// and set the delegate for this non-shown
So you need to remove let vc = RatingsView() and set the delegate for
rates.ratingDelegate = self

Swift: Screen Freezing from time to time when Dismissing a ViewController displaying a picture over the current context

I am having some troubles with: The screen freezing from time to time when Dismissing the ViewController displaying a picture over the current context.
May someone provide me some insights on how to fix this problem?
A sample of my codes is found below:
import UIKit
class ViewControllerCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.white
addSubview(showPhotoButton)
showPhotoButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 200).isActive = true
showPhotoButton.bottomAnchor.constraint(equalTo: topAnchor, constant: 160).isActive = true
showPhotoButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
showPhotoButton.widthAnchor.constraint(equalToConstant: 70).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var showPhotoButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Show", for: .normal)
button.addTarget(self, action: #selector(showSale), for: .touchUpInside)
button.setTitleColor(UIColor(r: 120, g: 80, b: 255), for: .normal)
return button
}()
#objc func showSale() {
let popupViewController = PopupViewController()
popupViewController.modalPresentationStyle = .overCurrentContext
popupViewController.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
window!.rootViewController?.present(PopupViewController, animated: true, completion: nil)
}
}
import UIKit
class SalePopupViewController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = UIColor(white: 1, alpha: 0.60)
view.isOpaque = false
view.addSubview(rebateImage)
view.addSubview(dismissButton)
dismissButton.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
dismissButton.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
dismissButton.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
dismissButton.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
rebateImage.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
rebateImage.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -35).isActive = true
rebateImage.heightAnchor.constraint(equalToConstant: 290).isActive = true
rebateImage.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true
}
let dismissButton: UIButton = {
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(dismissPopup), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let rebateImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.layer.masksToBounds = false
image.layer.cornerRadius = 2
image.contentMode = .scaleAspectFill
image.clipsToBounds = true
image.image = UIImage(named: "SaleCostco")
return image
}()
#objc func dismissPopup() {
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
}
}
Your asynchronous code doesn't make sense. You dispatch onto a global queue and then immediately back onto the main thread. Try simply changing the implementation of dismissPopup() to this:
#objc func dismissPopup() {
dismiss(animated: true, completion: nil)
}

Resources