I'm busy making an app with an account page. I want that users can logon via that page and as soon as they have done so successfully that the page reloads to display their account information rather than the standard message stating that they have to logon to make use of the page.
However when I get sent back to the account page from logging on the view doesn't really update. So therefore I am wondering if I can't reload the view after certain buttons are pressed that can check again wether the user is logged on or not and deal accordingly.
if you want to trigger layouting or just drawing there is setNeedsLayout and setNeedsDisplay
There is no built-in method to reload custom data (on iOS)
so do a reload and inside a reload -- call setNeedsDisplay
import UIKit
protocol MyViewDelegate {
func viewString() -> String;
}
class MyView : UIView {
var myViewDelegate : MyViewDelegate?
private var str : String?
func reloadData() {
if myViewDelegate != nil {
str = myViewDelegate!.viewString()
}
self.setNeedsDisplay()
}
override func drawRect(rect: CGRect) {
UIColor.whiteColor().setFill()
UIRectFill(self.bounds)
if str != nil {
let ns = str! as NSString
ns.drawInRect(self.bounds, withAttributes: [NSForegroundColorAttributeName: UIColor.blueColor(), NSFontAttributeName: UIFont.systemFontOfSize(10)])
}
}
}
class ViewController: UIViewController, MyViewDelegate {
func viewString() -> String {
return "blabla"
}
var v : MyView!
override func viewDidLoad() {
super.viewDidLoad()
v = MyView(frame: self.view.bounds)
self.view.addSubview(v)
v.myViewDelegate = self;
}
override func viewWillAppear(animated: Bool) {
v.reloadData()
}
}
In Swift use this,
If you wants to reload the UIView present in the viewController just use NotificationCenter.
In ViewController class add this in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
//Trigger notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "update"), object: nil)
}
In UIView class add Observer for the notification
required init?(coder aDecoder: NSCoder) {
//Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(updateList), name: NSNotification.Name(rawValue: "update"), object: nil)
}
#objc func updateList(){
//write Reload data here......
tableView.reloadData()
}
The Swift have really advanced itself and for many people if they don't know we can refresh the whole view with just one simple line of code.
viewWillAppear(true)
Related
In my app, I've got a scenario where I need to switch the UI theme design based on user type. For eg: In my Type1 user flow it goes like Registration Screen -> HomePage Screen and in my Type 2 user it should go like Registration Screen-> Contact Screen -> Home Page Screen. And the UI design and themes are different in the case of Type 2 user. For achieve this, below is my sample code flow which is implemented currently.
RegistrationViewController
(This view is available for both users but UI theme is different, like navigation bar color, bg color, button color, fonts, images etc)
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews(){
if Utilities.isUserType1{
setupViewsForType1User() //Adds themes for type 1 user
} else {
setupViewsForType2User() //Adds themes for type 2 user
}
}
#IBAction func continueAction(_ sender: Any) {
if Utilities.isUserType1{
goToContactView() //Goes to ContactViewController
} else {
gotToHomeView() //Goes to HomeViewController
}
}
ContactViewController
(This view is only available for type1 user)
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews(){
//Setupviews
}
#IBAction func continueAction(_ sender: Any) {
gotToHomeView()
}
HomeViewController
(This view is available for both users but UI theme is different as mentioned in Registration)
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews(){
if Utilities.isUserType1{
setupViewsForType1User()
} else {
setupViewsForType2User()
}
}
This works fine, but the problem here is now I've defined an isUserType in Utilities and its not perfectly scalable. For each flow and UI change, I need to put an if-else condition based on this parameter. So, now if I have another user type that needs to be added in the future I would again need another if-else statement and switch UI and flow based on that.
Is there any better approach to solve this problem?
You can check for change Theme there
You need change dynamic theme based on different user so post notification and apply theme what you need
// We create a model
struct Theme {
let theme: String
let fontColor: UIColor
let alpha: CGFloat
}
// We need a protocol for we don't want all view controller listen theme
protocol Themeable: class {
func listenTheme()
func didThemeChange(theme: Theme)
}
// Global notification name
let themeableNotificationName = Notification.Name(rawValue: "ThemeableNotification")
// Our protocol extension and observer notification
extension Themeable where Self: UIViewController {
func listenTheme() {
NotificationCenter.default.addObserver(forName: themeableNotificationName, object: nil, queue: nil) { [weak self] notification in
guard let theme = notification.object as? Theme else { return }
self?.didThemeChange(theme: theme)
}
}
}
// Notification sender themeController
class NotifyThemeController: UIViewController {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Create a model and post
NotificationCenter.default.post(name: themeableNotificationName, object: Theme(theme: "Lorem", fontColor: .red, alpha: 1.0), userInfo: nil)
}
}
// YViewController
class YViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// We need call this method for observer
listenTheme()
}
}
// YViewController conforms Themeable
extension YViewController: Themeable {
func didThemeChange(theme: Theme) {
// TODO UI
}
}
// ZViewController
class ZViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// We need call this method for observer
listenTheme()
}
}
// ZViewController conforms Themeable
extension ZViewController: Themeable {
func didThemeChange(theme: Theme) {
// TODO UI
}
}
Have Fun!
Problem relates to this.
I'm trying to use a custom view that I made that has two TextField inputs and a button, I made it using IB its xib, I've placed it in my story board and have it set to be at the bottom of my view.
The problem comes in when I want to make ContactInfoView the keyboards accessory view.
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet var cameraPreview: UIView!
#IBOutlet weak var ContactInfoView: KeyboardAccessoryView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
//cameraPreviewLayer!.frame = cameraPreview.bounds
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillAppear"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide"), name: UIKeyboardWillHideNotification, object: nil)
ContactInfoView.NameInput.inputAccessoryView = ContactInfoView
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
// keyboard stuff
var accessoryView = KeyboardAccessoryView(frame: CGRectZero);
override var inputAccessoryView: KeyboardAccessoryView {
return accessoryView
}
override func becomeFirstResponder() -> Bool {
return true
}
override func canBecomeFirstResponder() -> Bool {
return true
}
func keyboardWillAppear() {
print("Keyboard appeared")
}
func keyboardWillHide() {
print("Keyboard hidden")
}
}
ContactInfoView.NameInput.inputAccessoryView = ContactInfoView isn't placing the view on top of the keyboard.
I fooled out around with the code and it started to crash relating to the link provided, But trying that solution didn't work either.
According to the comment in the UITextView.h file:
#property (nullable, readwrite, strong) UIView *inputView;
#property (nullable, readwrite, strong) UIView *inputAccessoryView;
Presented when object becomes first responder. If set to nil, reverts to following responder chain. If set while first responder, will not take effect until reloadInputViews is called.
You should call reloadInputViews method after setting input.. property.
Yes, This Looks Little Tricky since everything seems fine and same as everyone and the Xcode Auto Suggestion and Completion Tools Helps you to complete the predicted "override" Functions..
Here Is A simple Mistakes that I made While Showing "inputAccessoryView"
I Entered Xcode Suggested Text For Completing "inputAccessoryView" And "inputAccessoryView" for Override Function.
But For Adding "override var inputAccessoryView: UIView?" Func its okay To Select from Xcode Suggestion / Auto Completion
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
For "override var canBecomeFirstResponder : Bool" you should make sure that
you type ": Bool" and not the "() -> Bool", Like I made mistakes couple of time.
So Please If you want You can type yourself or just Copy Below Code Paste in your Class Anywhere and it will work Smoothly.
override var inputAccessoryView: UIView? {
get {
let inputContainerView = UIView()
inputContainerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50)
inputContainerView.backgroundColor = UIColor.gray
let textfield = UITextField()
textfield.text = "asasfdfs df sdf sdf ds f"
textfield.frame = CGRect(x: 0, y: 0, width: inputContainerView.frame.size.width, height: 50)
inputContainerView.addSubview(textfield)
// You can add Any Other Objects Like UIButton, Image, Label //you want And Constraints too.
return inputContainerView
}
}
override var canBecomeFirstResponder : Bool {
return true
}
In the code above, you are assigning the property InputAccessoryView not the property inputAccessoryView. Case is important.
Assuming everything else is setup correctly, this should get it to work, but there is something I don't understand. Why is your text field/text view a property of your input accessory view? If NameInput is a subview of ContactInfoView, then setting ContactInfoView.NameInput.inputAccessoryView will not work.
I have a CenterViewController which contains a Game Controller. I want to add/remove a RulesViewController that the user can easily refer to as they play.
The RulesViewController appears and is dismissed fine. But the delegate.continueGame method is never called. I've added the protocol to RulesViewController. I've added a class extension to CenterViewController to handle the delegate. What am I missing?? Any help much appreciated...
Class CenterViewController: UIViewController {
private var controller: GameController
required init(coder aDecoder: NSCoder){
controller = GameController()
}
override func viewDidLoad() {
// add all the views here
let gameView = UIView(frame: CGRectMake(0,0, ScreenWidth, ScreenHeight))
self.view.addSubview(gameView)
controller.gameView = gameView
}
// method called when rules button on the gameView is pressed
func showRulesForLevel () {
let rulesViewController = storyboard!.instantiateViewControllerWithIdentifier("RulesViewController") as! RulesViewController
presentViewController(rulesViewController, animated: true, completion: nil)
// extension to the Class to handle the delegate
extension CenterViewController: RulesViewControllerDelegate {
//func to continue the game
func continueGame() {
controller.gameView.userInteractionEnabled = true
}
}
In the RulesViewController I have:
protocol RulesViewControllerDelegate {
func continueGame()
}
class RulesViewController: UIViewController {
var delegate: RulesViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// code to add a continue button which when pressed calls continueGameAction method
}
func continueGameAction() {
// dismiss the UIViewController so game can continue
self.dismissViewControllerAnimated(true, completion: nil)
// continue the game in CenterViewController
delegate?.continueGame()
}
}
BUT delegate?.continueGame() is never called.
Ok so you need to set the delegate in showRulesForLevel method like this:
rulesViewController.delegate = self
:)
I dragged and dropped progressindicator from main.xib on to a webview .
I need to show NSProgressIndicator on top of webview , instead of showing on the same webview.
The main idea is to stop the enduser to access webview until and unless NSProgressIndicator.stopAnimation(self) is done.
Can someone help me on this .
//In order to show a modelpanel on top of appdelegate.swift we need to //follow two steps.
//step1: create a cocoa class from NSWindowController as below.
import Cocoa
class ManuModalController: NSWindowController
{
#IBOutlet weak var progressIndicator1: NSProgressIndicator!
var mainW: NSWindow = NSWindow()
override init()
{
super.init()
}
override init(window: NSWindow!)
{
super.init(window: window)
}
required init?(coder: (NSCoder!))
{
super.init(coder: coder);
}
override func windowDidLoad()
{
super.windowDidLoad()
}
func beginSheet(mainWindow: NSWindow)
{
self.mainW = mainWindow;
NSApp.beginSheet(self.window!, modalForWindow: self.mainW, modalDelegate: self, didEndSelector:nil, contextInfo: nil);
progressIndicator1.startAnimation(self);
}
#IBAction func btnClicked(sender: AnyObject)
{
self.endSheet();
}
func endSheet()
{
NSApp.endSheet(self.window!);
self.window!.orderOut(mainW);
}
}
//step2 : create the object of modalcontroller in appdelegate.swift as below
var mdlwin = ManuModalController(windowNibName: "ManuModalController");
mdlwin.beginSheet(self.window);//displays the modalpanel
mdlwin.endSheet(); //closes the modal panel
I can not update my custom view when you turn the screen of my device.
I tried to do this:
override func viewDidLoad() {
super.viewDidLoad()
var myCustomView = Customview()
self.view.addsubview(myCustomView)
}
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
// Reload Data here
self.view.reloadData() // Incorrect
}
But get me error:
the method "UIView" does not have a method called reloadData
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
// Reload Data here
self.view.reloadData() // Incorrect
}
UIView is not a UITableView it doesn't have reloadData method out of the box.
You can implement this method manually -> have fun :)
You want to call reloadData() on the instance of your UITableView (assuming there is any). UIView does not provide such a method.
Edit to get notified when the screen orientation has changed, set up a notification:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didRotate:", name: UIDeviceOrientationDidChangeNotification, object: nil)
and implement the selector, e.g.
func didRotate(notification: NSNotification)
{
myTableView.reloadData()
}
I think it's work for you
objective-C
[self.view setNeedsDisplay];
swift
self.view.setNeedsDisplay()
override func viewDidLoad() {
super.viewDidLoad()
var myCustomView = Customview()
self.view.addsubview(myCustomView)
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
// Reload Data here
self.view.setNeedsDisplay()
}