Custom input accessory view Notification - uitextfield

At end of Advanced Notification Session 708 WWDC 2016. They talked about taking text input & action at same time.
Start at 24 min mark of WWDC session 708.
Where you take comment on party invite & accept or decline invite at same time. I tried to do that but am very unsuccessful.
class NotificationViewController: UIViewController, UNNotificationContentExtension {
var textField = UITextField(frame: CGRect(x: 0, y: 0, width: 100, height: 10))
override func viewDidLoad() {
super.viewDidLoad()
// Do any required interface initialization here.
}
override var canBecomeFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
let customView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
customView.backgroundColor = UIColor.red
textField.placeholder = "Type Comment here"
textField.textColor = UIColor.black
customView.addSubview(textField)
print(customView)
return customView
}
func didReceive(_ response: UNNotificationResponse, completionHandler completion: #escaping (UNNotificationContentExtensionResponseOption) -> Void) {
if response.actionIdentifier == "comment" {
becomeFirstResponder()
textField.becomeFirstResponder()
print("Its working")
if let textResponse = response as? UNTextInputNotificationResponse {
print(textResponse.userText)
completion(UNNotificationContentExtensionResponseOption.doNotDismiss)
}
completion(UNNotificationContentExtensionResponseOption.doNotDismiss)
}
}
}

Not sure exactly what your problem is, but I recently faced a similar task - and I've successfully implemented the solution from your referred WWDC talk.
My problem was that I didn't know how to dismiss the notification when using a custom inputAccessoryView. My solution was to save the completionHandler and then call it when a specific button in my custom inputAccessoryView is clicked.
First; create the function variable to store the completionHandler:
var savedCompletionBlock:(UNNotificationContentExtensionResponseOption) -> Void = { (UNNotificationContentExtensionResponseOption) in }
Then; in func didReceive(_ response: UNNotificationResponse, completionHandler completion: #escaping (UNNotificationContentExtensionResponseOption) -> Void) save the completion block:
savedCompletionBlock = completion
And finally; call it wherever you need to (e.g. on a button click):
func confirmPressed() {
savedCompletionBlock(.dismiss)
}
If this doesn't help you let me know :)

Related

UIViewControllerTransitioningDelegate presenting doesn't work (though dismissing does)

I am trying to implement a custom appearing/disappearing animation for a modal UIViewController in my app.
I have published the code showing this error here.
Here is the content related:
/// The view controller from which I'm trying to display the modal
class ViewController: UIViewController {
#IBAction func tapped() {
/// The modal showing
ModalTestViewController.show()
}
//...
}
/// The displayed modal
open class ModalTestViewController: TransitioningModalViewController {
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
// 1. when I put transitioningDelegate here, case 1
}
public required init?(coder: NSCoder) {
fatalError()
}
open override func viewDidLoad() {
super.viewDidLoad()
// 2. if I put transitioningDelegate here, case 2
}
#IBAction func tapped() {
// a tap on the overlayView of my modal
dismiss(animated: true)
}
static func show() {
let modal = ModalTestViewController()
modal.modalPresentationStyle = .overCurrentContext
DispatchQueue.main.async {
UIApplication.shared.delegate?.window??.rootViewController?.present(modal, animated: true)
}
}
}
/// The default modal view controller, which all modals in my app should inherit
open class TransitioningModalViewController: UIViewController {
// MARK: View Properties
#IBOutlet weak var overlayView: UIView!
}
extension TransitioningModalViewController: UIViewControllerTransitioningDelegate {
public func animationController(
forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerPresenter()
}
public func animationController(
forDismissed dismissed: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerDismisser()
}
}
private final class TransitioningModalViewControllerPresenter: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
private final class TransitioningModalViewControllerDismisser: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let fromViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.from
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
UIView.animate(
withDuration: duration
) {
fromViewController.overlayView.alpha = 0.0
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
The idea behind this is that the modal appearance should not be the usual bottom-to-top animation, but instead the overlay view should go from hidden to an alpha of 0,65.
Case 1: when I put transitioningDelegate = self in init(), the animation is killed and nothing happens.
Case 2: when I put it into the viewDidLoad(), the appearing animation is the default bottom-to-top one, but the disappearing one is the expecting one (with the overlay view vanishing).
It looks like something is wrong with the initial transitioningDelegate setting but I can't find what.
Thank you for your help!
In your original code, you are setting the delegate here:
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
transitioningDelegate = self
}
However, .present(...) is called before viewDidAppear(...), so the controller is presented with default slide-up animation.
Setting the delegate in init() doesn't work, because we have override the default presentation process... and the presented controller's view is never added to the view hierarchy.
This "quick fix" should do the job...
First, in ModalTestViewController, move setting the delegate to init():
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
}
then, in TransitioningModalViewControllerPresenter, add these lines before the animation:
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
// add these lines \/
// get the "from" view controller
let fromVC = transitionContext.viewController(forKey: .from)!
// get the "to" view controller's view
let toView = transitionContext.view(forKey: .to)!
// set the frame of the "to" view to the initialFrame (the current frame) of the "from" VC
toView.frame = transitionContext.initialFrame(for: fromVC)
// get the transition container view
let container = transitionContext.containerView
// add the "to" view to the view hierarchy
container.addSubview(toView)
// add these lines /\
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
Personally, to make this more flexible, I would get rid of your overlayView and set the alpha on the controller's view itself.

SwiftUI - Memory Management

OK, so I'm looking to improve memory within my app.
I have enabled Live Memory Allocation for my project and I'm using the Debug Graph Tool. I'm looking at the backtraces, and coming across issues, which in total honesty, does not make sense to me. I've, to the best of my knowledge, removed strong references, but I am getting issues with parts of my code where I just don't understand/see the issue. And example of this is:
struct ProductScrollView: UIViewRepresentable {
private let view = UIScrollView()
func makeCoordinator() -> Coordinator {
return ProductScrollView.Coordinator(parent1: self)
}
#State var currentPage: Int
#Binding var products: [ProductModel]
func makeUIView(context: Context) -> UIScrollView {
if view.superview == .none {
let childView = UIHostingController(rootView: ProductCell(products: $products, currentPage: currentPage, pageCount: products.count)
) <---- Debugger seems to indicate this with the message "Thread 1"
childView.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * CGFloat((products.count)))
view.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * CGFloat((products.count)))
view.addSubview(childView.view)
view.showsVerticalScrollIndicator = false
view.showsHorizontalScrollIndicator = false
view.contentInsetAdjustmentBehavior = .never
view.isPagingEnabled = true
view.delegate = context.coordinator
}
return view
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
uiView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * CGFloat((products.count)))
for i in 0..<uiView.subviews.count {
uiView.subviews[i].frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * CGFloat((products.count)))
}
}
class Coordinator: NSObject, UIScrollViewDelegate {
var parent: ProductScrollView
init(parent1: ProductScrollView){
parent = parent1
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let index = Int(scrollView.contentOffset.y / UIScreen.main.bounds.height)
parent.currentPage = index
}
deinit {
print("Coordiante scroll view desroted")
}
}
}
Which doesn't make complete sense to me, however, I believe the line:
return ProductScrollView.Coordinator(parent1: self)
May be the issue?
I'll even have the debugger point to functions that are performed on my onAppear method.
Could someone please help as to what I can do to better understand these issues and eliminate them?
Edit -
To delve even further, this line appears in my backtrace:
static var FEEDBACK = FeedbackAPI()
Is it due to creating a new instance everytime?
By design we need to instantiate view inside makeView, so SwiftUI manages UIView lift-cycle to be the same as representable view, like
struct ProductScrollView: UIViewRepresentable {
func makeCoordinator() -> Coordinator {
return ProductScrollView.Coordinator(parent1: self)
}
#State var currentPage: Int
#Binding var products: [ProductModel]
func makeUIView(context: Context) -> UIScrollView {
let view = UIScrollView() // << here !!
// ... other code
}

Can't get variable from Notification?

I want to write a project has a notification can print data which stored in main viewcontroller, but when notification fired, always get empty data.
In the ViewController
arrPerson = [String]()
override func viewDidLoad() {
super.viewDidLoad()
arrPerson.append("Peter")
arrPerson.append("Jack")
setNotification()
}
func printTheName() {
print("In printTheName")
print("arrPerson:\(arrPerson.count)")
for x in arrPerson {
print(x)
}
}
func setNotification() {
let notification = UNMutableNotificationContent()
notification.title = "title"
notification.subtitle = ""
notification.body = "body"
notification.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
let request = UNNotificationRequest(identifier: "check", content: notification, trigger: trigger)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if error != nil {
print("notificationCenter.add ERROR:\(error)")
// Handle any errors.
}
}
}
In the Appdelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("In userNotificationCenter")
ViewController().printTheName()
}
The code will print out "userNotificationCenter","In printTheName","arrPerson:0".
I need get data from notification. Why arrPerson.count is 0 when call printTheName from notification?
Try to programatically instantiate the viewController using a window in your appdelegate.
You will be able to fetch the count then in your notification center.Or since your viewController class is the landing screen, Call the userNotifationCenter method in your main Class and not here.

Adding interstitial ads into SpriteKit game swift

I'm trying to implement interstitial ads with iAd into my spriteKit game. My code is as follows:
class MainViewController: UIViewController, ADInterstitialAdDelegate {
var interAd = ADInterstitialAd()
var interAdView: UIView!
var closeButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
override func viewDidAppear(animated: Bool) {
closeButton.frame = CGRectMake(10, 10, 20, 20)
closeButton.layer.cornerRadius = 10
closeButton.setTitle("x", forState: .Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
closeButton.backgroundColor = UIColor.whiteColor()
closeButton.layer.borderColor = UIColor.blackColor().CGColor
closeButton.layer.borderWidth = 1
closeButton.addTarget(self, action: "close:", forControlEvents: UIControlEvents.TouchDown)
}
func close(sender: UIButton) {
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
func loadAd() {
println("load ad")
interAd = ADInterstitialAd()
interAd.delegate = self
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
println("ad did load")
interAdView = UIView()
interAdView.frame = self.view!.frame
view!.addSubview(interAdView)
interAd.presentInView(interAdView)
UIViewController.prepareInterstitialAds()
interAdView.addSubview(closeButton)
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
println("failed to receive")
println(error.localizedDescription)
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
This code delivers me more problems than it does results. Unfortunately I am a beginner at implementing any sort of ads into an app and I don't know how I can change any of this in my favour.
One issue I get is that output traces ad did load (as you can see thats supposed to be traced once the ad is presented) however when ad did load is traced no ad is presented. I wait for at least 3 minutes but nothing happens.
Another issue I have is this WARNING: More than 10 instances of ADBannerView or ADInterstitialView currently exist. This is a misuse of the iAd API, and ad performance will suffer as a result. This message is printed only once., but this is printed after the function to load the ad is called once or twice.
Any help is more than appreciated.

Swift- Set Visible menucontroller

I'm trying to display a UimenuController but I can not view it. how can I do?
let MenuController: UIMenuController = UIMenuController.sharedMenuController()
MenuController.menuVisible = true
MenuController.arrowDirection = UIMenuControllerArrowDirection.Down
MenuController.setTargetRect(CGRectMake(100, 80, 50, 50), inView: self.view)
let MenuItem_1: UIMenuItem = UIMenuItem(title: "Menu", action: "delete:")
let MenuItems: NSArray = [delete]
MenuController.menuItems = MenuItems
In order to actually have the menu to display you need to do the following:
Call becomeFirstResponder() before you get your sharedMenuController
Call menu.setMenuVisible(true, animated: true) at the end
Override the canBecomeFirstResponder function
Override the canPerformAction function
Write the function for the selector
Here is an example
func someFunc() {
becomeFirstResponder()
var menu = UIMenuController.sharedMenuController()
var deleteItem = UIMenuItem(title: "Delete me", action: Selector("deleteLine"))
menu.menuItems = [deleteItem]
menu.setTargetRect(CGRectMake(100, 80, 50, 50), inView: self)
menu.setMenuVisible(true, animated: true)
}
func deleteLine() {
//Do something here
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
// You need to only return true for the actions you want, otherwise you get the whole range of
// iOS actions. You can see this by just removing the if statement here.
if action == Selector("deleteLine") {
return true
}
return false
}
Swift 3 version code:
func someFunc() {
becomeFirstResponder()
var menu = UIMenuController.shared
var deleteItem = UIMenuItem(title: "Delete me", action: #selector(ViewController.deleteLine))
menu.menuItems = [deleteItem]
menu.setTargetRect(CGRect(x: 0.0, y: 0.0, width: 20, height: 20), in: self)
menu.setMenuVisible(true, animated: true)
}
func deleteLine() {
//Do something here
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
// You need to only return true for the actions you want, otherwise you get the whole range of
// iOS actions. You can see this by just removing the if statement here.
if action == #selector(ViewController.deleteLine) {
return true
}
return false
}
One more important thing is action for UIMenuItem should be implemented.

Resources