I have static UItableview to hold user form. I added header view in the table to show email validation files my problem is the header does not show a smooth transition between hiding/show and overlap with the first row
I want to ask how I can fix the hight of the table header view and does not make it overlap
code
#IBOutlet weak var errorView: UIView!
#IBAction func next(_ sender: Any) {
let newUserEmail = self.txtfEmail.text
if isValidEmail(newUserEmail!) {
performSegue(withIdentifier: "addInventryToNewUser", sender: self)
}
else {
cellemail.layer.borderWidth = 2.0
cellemail.layer.borderColor = UIColor.red.cgColor
let f = errorView.frame;
errorView.frame = CGRect(x: f.origin.x, y: f.origin.y, width: f.width, height: 21);
errorView.isHidden = false
lblError.text = "❌ Invalid email address."
}
}
You need to apply some animation for a smooth transition. Here's how:
#IBAction func next(_ sender: Any) {
let newUserEmail = self.txtfEmail.text
if isValidEmail(newUserEmail!) {
performSegue(withIdentifier: "addInventryToNewUser", sender: self)
}
else {
cellemail.layer.borderWidth = 2.0
cellemail.layer.borderColor = UIColor.red.cgColor
UIView.animate(withDuration: 0.5) {
self.errorView.frame.size.height = 21
}
errorView.isHidden = false
lblError.text = "❌ Invalid email address."
}
}
I have no enough reputation to add a comment, but the answer on "how to hide it after 5 seconds?" Is:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// your code to hide view here
}
I will combine the two answer:
#IBAction func next(_ sender: Any) {
let newUserEmail = self.txtfEmail.text
if isValidEmail(newUserEmail!) {
performSegue(withIdentifier: "addInventryToNewUser", sender: self)
}
else {
cellemail.layer.borderWidth = 2.0
cellemail.layer.borderColor = UIColor.red.cgColor
UIView.animate(withDuration: 0.5) {
self.errorView.frame.size.height = 21
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// your code to hide view here
}
errorView.isHidden = false
lblError.text = "❌ Invalid email address."
}
}
Related
I have a simple media player and I'm trying to make it change the artwork image as the songs change. With the code I have now it will display the artwork when you hit play but when I hit the next button to skip to the next item it stays the same unless you hit another button.
How can I make the UIImageView image change as the song media item changes?
import UIKit
import MediaPlayer
class ViewController: UIViewController {
#IBOutlet weak var coverImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createQueue()
}
func showArt(){
coverImageView.image =
myMediaPlayer.nowPlayingItem?.artwork!.image(at: CGSize.init(width: 500, height: 500))
coverImageView.isUserInteractionEnabled = true
}
#IBAction func playButton(_ sender: UIButton) {
togglePlay(on: sender)
showArt()
}
#IBAction func backButton(_ sender: UIButton) {
back()
}
#IBAction func nextButton(_ sender: UIButton) {
skip()
}
}
My other functions are as followed:
import MediaPlayer
let myMediaPlayer = MPMusicPlayerApplicationController.systemMusicPlayer
let playDrake = MPMediaPropertyPredicate(value: "Drake", forProperty: MPMediaItemPropertyArtist, comparisonType: MPMediaPredicateComparison.equalTo)
let myFilterSet: Set<MPMediaPropertyPredicate> = [playDrake]
func createQueue() {
let drakeQuery = MPMediaQuery(filterPredicates: myFilterSet)
myMediaPlayer.setQueue(with: drakeQuery)
}
func skip() {
myMediaPlayer.skipToNextItem()
}
func back() {
if myMediaPlayer.currentPlaybackTime > 0.05 {
myMediaPlayer.skipToPreviousItem()
} else if myMediaPlayer.currentPlaybackTime < 0.05 {
myMediaPlayer.skipToBeginning()
} else {
//do nothing
}
}
func togglePlay(on: UIButton) {
if myMediaPlayer.playbackState.rawValue == 2 || myMediaPlayer.playbackState.rawValue == 0 {
on.setTitle("Pause", for: UIControlState.normal)
myMediaPlayer.play()
} else if myMediaPlayer.playbackState.rawValue == 1{
on.setTitle("Play", for: UIControlState.normal)
myMediaPlayer.pause()
} else {
// do nothing
}
}
Try loading the image asynchronously
DispatchQueue.global(qos: .background).async {
myMediaPlayer.nowPlayingItem?.artwork!.image(at: CGSize.init(width: 500, height: 500))
}
I have chatting app. I decide to create SlideShow tutorial for it. Now I have problem. How can I run TutorialVC just once when user install the app?
Usually app starts with AuthVC. Now I want to run tutorialVC just once, and then when user close app and run it again, from auth like usually.
My tutorial VC:
class TutorialViewController: UIViewController, UIScrollViewDelegate {
#IBAction func understandButtonAction(_ sender: Any) {
}
#IBOutlet weak var understandButton: UIButton!
#IBOutlet weak var tutorialPageControl: UIPageControl!
#IBOutlet weak var tutorialScrollView: UIScrollView!
var images: [String] = ["1","2","3","4"]
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
override func viewDidLoad() {
super.viewDidLoad()
setup()
addSlider()
setupButton()
}
//===============================
//EVTAuthorizationViewController
//===============================
override func viewWillAppear(_ animated: Bool) {
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelStatusBar
}
override func viewWillDisappear(_ animated: Bool) {
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelNormal
}
//AddButton
func setupButton(){
understandButton.layer.cornerRadius = 20
}
#IBAction func buttonAction(_ sender: Any?) {
print("Successful")
}
//ScrollBars
func setup(){
self.understandButton.isHidden = true
tutorialScrollView.showsHorizontalScrollIndicator = false
tutorialScrollView.showsVerticalScrollIndicator = false
}
//ScrollView method
//=============================
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
tutorialPageControl.currentPage = Int(pageNumber)
if tutorialPageControl.currentPage == 3{
self.understandButton.isHidden = false
}else{
self.understandButton.isHidden = true
}
}
//Addslider with photo
func addSlider(){
tutorialPageControl.numberOfPages = images.count
for index in 0..<images.count{
let xPos = self.view.frame.size.width * CGFloat(index)
frame.origin.x = tutorialScrollView.frame.size.width * CGFloat(index)
//frame.size = view.frame.size
let imageView = UIImageView(frame: CGRect(x: xPos, y: 0, width: self.view.frame.width, height: self.view.frame.size.height))
imageView.image = UIImage(named: images[index])
imageView.contentMode = .scaleAspectFill
self.tutorialScrollView.addSubview(imageView)
}
tutorialScrollView.contentSize = CGSize(width: (view.frame.size.width * CGFloat(images.count)), height: view.frame.size.height)
tutorialScrollView.delegate = self
}
}
Use userDefaults. I suppose the understandButton is the button the user hits to skip the tutorial, so when when it's tapped set a true bool value for a key that you are going to use, here I've chosen "tutorial presented":
#IBAction func understandButtonAction(_ sender: Any) {
UserDefaults.standard.set(true, forKey: "tutorial presented")
}
and when the app launches, in the AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let window = (UIApplication.shared.delegate as! AppDelegate).window
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
if UserDefaults.standard.bool(forKey: "tutorial presented") == true {
let controller = storyboard.instantiateViewController(withIdentifier: "Your Navigation controller name")
window?.rootViewController = tutorialViewController()
} else {
let tutorial = storyboard.instantiateViewController(withIdentifier: "Your tutorial controller name")
window?.rootViewController = tutorial
}
window?.makeKeyAndVisible()
return true
}
You can use a flag and store it via NSUserDefaults.
extension UserDefaults {
private static let didLaunchAppOnceKey = "didLaunchAppOnce"
var didLaunchAppOnce: Bool {
get { return bool(forKey: UserDefaults.didLaunchAppOnceKey) }
set { set(newValue, forKey: UserDefaults.didLaunchAppOnceKey) }
}
}
Then before presenting your view controller, check if the flag is set:
if !UserDefaults.standard.didLaunchAppOnce {
// Set the flag to true, so on next launch, we won't enter in the if again
UserDefaults.standard.didLaunchAppOnce = true
// Present your VC
…
}
I have a simple task but I don't seem to be able to figure it out. I have a UIViewController that has 5 UITextFields. 2 of those UITextFields are always visible. The other 3 are being shown when the user hits a UIButton. My goal is to disable the UIButton so it's obvious to the user that they cannot "add" more UITextFields after ALL of the UITextFields are visible. I tried to do this:
#IBAction func addTextFieldPressed(_ sender: UIButton) {
if !thirdChoiceTextField.isHidden, !forthChoiceTextField.isHidden, !fifthChoiceTextField.isHidden {
addTextFieldButton.isEnabled = false
}
if thirdChoiceTextField.isHidden {
UIView.animate(withDuration: 0.2) {
self.thirdChoiceTextField.isHidden = false
}
}
else if forthChoiceTextField.isHidden {
UIView.animate(withDuration: 0.2) {
self.forthChoiceTextField.isHidden = false
}
}
else {
UIView.animate(withDuration: 0.2) {
self.fifthChoiceTextField.isHidden = false
}
}
}
But it doesn't work. You're able to add the UITextFields. There's also a UIButton that allows the user to remove the 3 added UITextField. So, I have to make sure that if all of the text fields are shown, the UIButton responsible for adding more UITextFields would be disabled, but if any of the UITextFields is removed (.isHidden = true), the button should be enabled once again.
UPDATE: This is the code that runs after the user hits the "hide" button and it basically hides either 3rd, 4th or 5th UITextField
#objc func hideTextField(_ sender: UIButton) {
if let field = sender.superview?.superview as? UITextField, !field.isHidden {
UIView.animate(withDuration: 0.2) {
field.text = ""
field.isHidden = true
}
}
}
There is a missing else condition.
#IBAction func addTextFieldPressed(_ sender: UIButton) {
if !thirdChoiceTextField.isHidden, !forthChoiceTextField.isHidden, !fifthChoiceTextField.isHidden {
addTextFieldButton.isEnabled = false
}
else{
addTextFieldButton.isEnabled = true
}
if thirdChoiceTextField.isHidden {
UIView.animate(withDuration: 0.2) {
self.thirdChoiceTextField.isHidden = false
}
}
else if forthChoiceTextField.isHidden {
UIView.animate(withDuration: 0.2) {
self.forthChoiceTextField.isHidden = false
}
}
else {
UIView.animate(withDuration: 0.2) {
self.fifthChoiceTextField.isHidden = false
}
}
}
Update (try adding this to enable the button)
#objc func hideTextField(_ sender: UIButton) {
if let field = sender.superview?.superview as? UITextField, !field.isHidden {
UIView.animate(withDuration: 0.2) {
field.text = ""
field.isHidden = true
}
}
if !thirdChoiceTextField.isHidden, !forthChoiceTextField.isHidden, !fifthChoiceTextField.isHidden {
addTextFieldButton.isEnabled = false
}
else{
addTextFieldButton.isEnabled = true
}
}
I have VPMOTPView custom view in my `.xib' like this.
class VerifyOTP: UIView {
#IBOutlet weak var otpView: VPMOTPView!
var emailID = ""
var userID = ""
#IBAction func resendOTPAction(_ sender: UIButton) {
print("asdds")
}
override func awakeFromNib() {
super.awakeFromNib()
otpView.otpFieldsCount = 4
otpView.otpFieldDefaultBorderColor = UIColor.blue
otpView.otpFieldEnteredBorderColor = UIColor.red
otpView.otpFieldBorderWidth = 2
otpView.delegate = self
// Create the UI
otpView.initalizeUI()
}
}
extension VerifyOTP: VPMOTPViewDelegate {
func hasEnteredAllOTP(hasEntered: Bool) {
print("Has entered all OTP? \(hasEntered)")
}
func enteredOTP(otpString: String) {
print("OTPString: \(otpString)")
}
}
Then in my 'ViewController'
var verifyOTPView: VerifyOTP?
self.verifyOTPView = VerifyOTP.fromNib()
self.verifyOTPView?.frame = CGRect(x: 20, y: 0, width: screenSize.width - 40, height: screenSize.height / 3)
self.view.addSubview(self.verifyOTPView!)
But by this I can see my RESEND OTP button on screen but OTPVIEW is not displayed.
From the screenshot it doesn't look like you have any constraints set up for your views?
If not try adding constraints and see if that fixes the problem.
whenever I click a textfield inside the view, then click the other text field, the view disappears. Strange... Can anyone help?
I animate the view using facebook pop. Here is my animation engine code:
import UIKit
import pop
class AnimationEngine {
class var offScreenRightPosition: CGPoint {
return CGPoint(x: UIScreen.main.bounds.width + 250,y: UIScreen.main.bounds.midY - 75)
}
class var offScreenLeftPosition: CGPoint{
return CGPoint(x: -UIScreen.main.bounds.width,y: UIScreen.main.bounds.midY - 75)
}
class var offScreenTopPosition: CGPoint{
return CGPoint(x: UIScreen.main.bounds.midX,y: -UIScreen.main.bounds.midY)
}
class var screenCenterPosition: CGPoint {
return CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY - 75)
}
let ANIM_DELAY : Int = 1
var originalConstants = [CGFloat]()
var constraints: [NSLayoutConstraint]!
init(constraints: [NSLayoutConstraint]) {
for con in constraints {
originalConstants.append(con.constant)
con.constant = AnimationEngine.offScreenRightPosition.x
}
self.constraints = constraints
}
func animateOnScreen(_ delay: Int) {
let time = DispatchTime.now() + Double(Int64(Double(delay) * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: time) {
var index = 0
repeat {
let moveAnim = POPSpringAnimation(propertyNamed: kPOPLayoutConstraintConstant)
moveAnim?.toValue = self.originalConstants[index]
moveAnim?.springBounciness = 8
moveAnim?.springSpeed = 8
if (index < 0) {
moveAnim?.dynamicsFriction += 10 + CGFloat(index)
}
let con = self.constraints[index]
con.pop_add(moveAnim, forKey: "moveOnScreen")
index += 1
} while (index < self.constraints.count)
}
}
class func animateToPosisition(_ view: UIView, position: CGPoint, completion: ((POPAnimation?, Bool) -> Void)!) {
let moveAnim = POPSpringAnimation(propertyNamed: kPOPLayerPosition)
moveAnim?.toValue = NSValue(cgPoint: position)
moveAnim?.springBounciness = 8
moveAnim?.springSpeed = 8
moveAnim?.completionBlock = completion
view.pop_add(moveAnim, forKey: "moveToPosition")
}
}
Then here is my viewcontroller code where the view is inside in:
import UIKit
import pop
class LoginVC: UIViewController, UITextFieldDelegate {
override var prefersStatusBarHidden: Bool {
return true
}
#IBOutlet weak var emailLoginVCViewConstraint: NSLayoutConstraint!
#IBOutlet weak var emailLoginVCView: MaterialView!
#IBOutlet weak var emailAddressTextField: TextFieldExtension!
#IBOutlet weak var passwordTextField: TextFieldExtension!
var animEngine : AnimationEngine!
override func viewDidAppear(_ animated: Bool) {
self.emailLoginVCView.isUserInteractionEnabled = true
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.bringSubview(toFront: emailAddressTextField)
self.animEngine = AnimationEngine(constraints: [emailLoginVCViewConstraint])
self.emailAddressTextField.delegate = self
self.passwordTextField.delegate = self
emailAddressTextField.allowsEditingTextAttributes = false
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if (textField === emailAddressTextField) {
passwordTextField.becomeFirstResponder()
} else if (textField === passwordTextField) {
passwordTextField.resignFirstResponder()
} else {
// etc
}
return true
}
#IBAction func emailTapped(_ sender: AnyObject) {
AnimationEngine.animateToPosisition(emailLoginVCView, position: AnimationEngine.screenCenterPosition, completion: { (POPAnimation, Bool)
in
})
}
#IBAction func exitTapped(_ sender: AnyObject) {
AnimationEngine.animateToPosisition(emailLoginVCView, position: AnimationEngine.offScreenRightPosition, completion: { (POPAnimation, Bool)
in
})
}
}
Last here is my hierchy and options: (my view's name is emailLoginVCView). Also when I was debugging when I clicked another textfield I set a breakpoint so I got this info: enter image description here
I have a constraint that binds the center of the login view with the center of the main screen
when I create the AnimationEngine,I pass it that constraint, and it sets its constant to be the offScreenRightPosition.x
when I bring up the email login sheet, I'm not changing the constant of the constraint; I'm just changing the position of the view
which means that autolayout thinks it’s supposed to still be offscreen
when the second textfield becomes active, that’s somehow triggering auto-layout to re-evaluate the constraints, and it sees that the login view’s position doesn’t match what the constraint says it should be so....
Autolayout moves it offscreen
So if I add this in emailTapped(_:), the problem goes away :)
#IBAction func emailTapped(_ sender: AnyObject) {
AnimationEngine.animateToPosisition(emailLoginVCView, position: AnimationEngine.screenCenterPosition, completion: { (POPAnimation, Bool)
in
self.emailLoginVCViewConstraint.constant = 0
})
}