I have a custom view CustomSegmentedControl in my app which have its protocol with changed index function as below,
protocol SegmentControllerDelegate:class {
func indexChanged(index : Int)
}
class CustomSegmentedControl: UIView {
weak var delegate : SegmentControllerDelegate?
#objc func buttonAction(sender:UIButton) {
for (buttonIndex, btn) in buttons.enumerated() {
btn.setTitleColor(textColor, for: .normal)
if btn == sender {
let selectorPosition = frame.width/CGFloat(buttonTitles.count) * CGFloat(buttonIndex)
selectedIndex = buttonIndex
//delegate?.changeToIndex(index: buttonIndex)
if(delegate != nil){
delegate?.indexChanged(index: buttonIndex)
}else{
print(buttonIndex)
}
UIView.animate(withDuration: 0.3) {
self.selectorView.frame.origin.x = selectorPosition
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}
}
I added a view to my ViewController Mail and set the class to my custom view and make outlet for it and use the protocol as well like below,
import UIKit
class Mail: UIViewController, SegmentControllerDelegate {
func indexChanged(index: Int) {
print(index)
switch index {
case 0:
container.bringSubviewToFront(inbox)
break
case 1:
container.bringSubviewToFront(outbox)
break
default:
break
}
}
#IBOutlet weak var segment: CustomSegmentedControl!{
didSet{
segment.setButtonTitles(buttonTitles: ["First","Second"])
segment.selectorTextColor = .orange
segment.selectorViewColor = .orange
}
}
#IBOutlet weak var container: UIView!
var inbox : UIView!
var outbox : UIView!
override func viewDidLoad() {
super.viewDidLoad()
segment.delegate = self
inbox = Inbox().view
outbox = GPS().view
container.addSubview(inbox)
container.addSubview(outbox)
}
But the protocol is always nil and the function never be called !?, What Im missing here ?
Any help will be much appreciated
Related
I have used blocks in cell for getting switch value but now my problem is that deinit not called where i used the blocks. it is completely working previously but in swift 3.0 it is not working.
My switch cell :
import UIKit
class CellSwitch: UITableViewCell {
#IBOutlet weak var objSwitch: UISwitch!
#IBOutlet weak var btnInfo: UIButton!
#IBOutlet weak var lblTitle: UILabel!
var blockSwitch_Change : ((_ isOn:Bool) -> Void)!
var blockBtn_Clicked : (() -> Void)!
override func awakeFromNib() {
super.awakeFromNib()
self.lblTitle.font = Font.init(Font.FontType.custom(Font.FontName.NotoSans_Regular), size: Font.FontSize.standard(Font.StandardSize.Regular)).instance
// Initialization code
}
//MARK:- switch object change
#IBAction func switch_ValChanged(_ obj:UISwitch){
self.blockSwitch_Change?(obj.isOn)
}
//MARK:- button clicked
#IBAction func btnInfo_Clicked(_ sender: UIButton) {
self.blockBtn_Clicked?()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Uses of this cell
let cell = tableView.dequeueReusableCell(withIdentifier: CellSwitch.identifier) as? CellSwitch
cell?.lblTitle.textColor = Color.custom(hexString: objModel.titleLblColor, alpha: 1.0).value
cell?.lblTitle.text = objModel.strTitle
cell?.objSwitch.isOn = objModel.isOn
cell?.btnInfo.isHidden = !objModel.isInfoBtn
cell?.blockBtn_Clicked = {
print("button clicked")
}
cell?.blockSwitch_Change = { (isOn) in
print("switch value changed \(isOn)")
}
if objModel.isEnable == false
{
cell?.isUserInteractionEnabled = false
cell?.contentView.alpha = 0.5
}
else
{
cell?.isUserInteractionEnabled = true
cell?.contentView.alpha = 1.0
}
return cell!
Also if i comment this two blocks then my deinit will called.
It sounds like you are creating a retain cycle by referencing the view controller strongly inside the blocks. Instead you should create a weak reference to the vc to use. This is my preferred method
cell?.blockBtn_Clicked = { [weak self]
print("button clicked")
self?.viewModel.//do something
}
cell?.blockSwitch_Change = { [weak self] (isOn) in
print("switch value changed \(isOn)")
self?.viewModel.//do something
}
The [weak self] portion will pass a weak reference of self into the block, although note this reference is now an optional. You can then use optional chaining or unwrap it after that.
I have the following issue. I use RxSwift to bind my datasource to an UITableView like this:
self.presenter.posts
.bind(to: self.tableView.rx.items(cellIdentifier: "ListPostsCell")) { index, model, cell in
if let cell = cell as? ImagePostTableViewCell {
cell.viewModel = model
cell.actionButton.rx.action = self.action(for: model.postId, index: index)
}
}.disposed(by: self.bag)
When the tableview reaches the end of the datasource count I request more data and update the datasource (infinite scroll) like this:
self.tableView.rx.willDisplayCell
.map { $0.indexPath.item }
.distinctUntilChanged()
.withLatestFrom(self.presenter.posts) { (item, posts) -> Bool in
return item == posts.count - 3
}
.filter { $0 }
.withLatestFrom(self.presenter.posts)
.map { UsersPosts.Request(after: $0.last?.postId) }
.bind(to: self.usersPostsInteractor.receiveUsersPosts.inputs)
.disposed(by: self.bag)
This works fine and I can scroll infinite as expected. The problem is within my Cell:
class ImagePostTableViewCell: UITableViewCell {
#IBOutlet weak var postImageView: UIImageView!
#IBOutlet weak var haiScoreLabel: UILabel!
#IBOutlet weak var lastVoteLabel: UILabel!
#IBOutlet weak var actionButton: UIButton!
#IBOutlet weak var activtiyIndicator: UIActivityIndicatorView!
var bag: DisposeBag?
var viewModel: ImagePostTableViewCellViewModel? {
didSet {
let bag = DisposeBag()
guard let vm = viewModel else { return }
vm.image.asDriver(onErrorJustReturn: UIImage())
.drive(onNext: { image in
self.updateAppearanceFor(image)
}).disposed(by: bag)
self.haiScoreLabel.text = vm.haiScoreText
self.lastVoteLabel.text = vm.lastVoteText
self.actionButton.setTitle(vm.actionLabelText, for: .normal)
self.activtiyIndicator.startAnimating()
self.bag = bag
}
}
func updateAppearanceFor(_ image: UIImage?, animated: Bool = true) {
DispatchQueue.main.async {
if animated {
UIView.animate(withDuration: 0.5) {
self.displayImage(image)
}
} else {
self.displayImage(image)
}
}
}
private func displayImage(_ image: UIImage?) {
if let image = image {
self.show(image: image)
} else {
self.hide()
}
}
private func show(image: UIImage) {
self.postImageView.image = image
self.postImageView.alpha = 1.0
self.haiScoreLabel.alpha = 1.0
self.lastVoteLabel.alpha = 1.0
self.activtiyIndicator.stopAnimating()
}
private func hide() {
self.postImageView.image = nil
self.postImageView.alpha = 0
self.haiScoreLabel.alpha = 0
self.lastVoteLabel.alpha = 0
self.activtiyIndicator.startAnimating()
}
override public func prepareForReuse() {
super.prepareForReuse()
self.displayImage(.none)
self.viewModel = nil
self.bag = nil
}
}
As you can see there is an animation which ends up in an super smooth scrolling behaviour - but because of the datasource update, with every reload the animation starts and unchanged content flashes because of the animation.
In the end I want to avoid the reload for the visible cell but I really desperate here.
Any hints to follow? Thanks in advance!!!
I am loading a views from nib and i am successfully loaded but first one is loaded perfect but in second view getting nil outlets. below is my code.
Set a global variable to loadview by default is is 0 and it is increase when i press next button.
User is struct here :
struct User {
static var quizIndexNo = 0
}
This method is in my ViewController
let moptViewInstance = MOPT()
let rmkViewInstance = RMK()
getting view name from this array:
let viewName = ["MOPT","RMK"]
func loadQuizView(){
if viewName[User.quizIndexNo] == "MOPT" {
for views in vwOptionView.subviews {
views.removeFromSuperview()
}
moptViewInstance.prepareScreenWithView(navigationController: UINavigationController(), viewSize: self.vwOptionView, viewController: self)
} else if viewName[User.quizIndexNo] == "RMK" {
for views in vwOptionView.subviews {
views.removeFromSuperview()
}
rmkViewInstance.prepareScreenWithView(navigationController: UINavigationController(), viewSize: self.vwOptionView, viewController: self)
}
}
// NIB View Classes
class MOPT: UIView {
#IBOutlet weak var btnPrevious: UIButton!
#IBOutlet weak var btnNext: UIButton!
var nibView: UIView!
var instanceOfQuizController : QuizCointroller!
func prepareScreenWithView(navigationController: UINavigationController, viewSize: UIView, viewController: UIViewController) {
nibView = Bundle.main.loadNibNamed("MOPT", owner: self, options: nil)?[0] as! UIView
nibView.tag = 200
var tempRect = nibView.frame
print(tempRect)
if User.quizIndexNo == 0 {
btnPrevious.isHidden = true
}
if User.quizIndexNo == 0 {
btnNext.setTitle("Test", for: .normal)
}
print("Index No is: \(User.quizIndexNo) and \(Sequence.totalQuizQuestionCout)")
if User.quizIndexNo == Sequence.totalQuizQuestionCout - 1 {
btnNext.setTitle("Submit", for: .normal)
}
instanceOfQuizController = viewController as! QuizCointroller
tempRect = CGRect(x: 0, y: 0, width: viewSize.bounds.width, height: viewSize.bounds.height)
viewSize.addSubview(self.nibView)
self.nibView.frame = tempRect
}
#IBAction func btnPrev(_ sender: UIButton) {
User.quizIndexNo -= 1
Sequence.instanceOfQuizControllerTest.previousButtonPressed()
}
// Next button will reload next view
#IBAction func btnNext(_ sender: UIButton) {
User.quizIndexNo += 1
// here sequence is global variable of type ViewController
Sequence.instanceOfQuizControllerTest.nextButtonPressed()
}
}
Second NibView Class
class RMK: UIView {
#IBOutlet weak var btnPrevious: UIButton!
#IBOutlet weak var btnNext: UIButton!
var nibView: UIView!
var instanceOfQuizControllerTest : QuizCointroller!
func prepareScreenWithView(navigationController: UINavigationController, viewSize: UIView, viewController: UIViewController) {
nibView = Bundle.main.loadNibNamed("RMK", owner: self, options: nil)?[0] as! UIView
nibView.tag = 200
var tempRect = nibView.frame
print(tempRect)
if User.quizIndexNo == 1 {
btnPrevious.isHidden = true //app is crashing here when i am trying to set title Button is showing nil
}
if User.quizIndexNo == 1 {
btnNext.setTitle("Submit", for: .normal)
}
instanceOfQuizControllerTest = viewController as! QuizCointroller
print("Quiz ViewController instatnce \(instanceOfQuizControllerTest)")
tempRect = CGRect(x: 0, y: 0, width: viewSize.bounds.width, height: viewSize.bounds.height)
viewSize.addSubview(self.nibView)
self.nibView.frame = tempRect
}
#IBAction func btnPrev(_ sender: UIButton) {
User.quizIndexNo -= 1
Sequence.instanceOfQuizControllerTest.previousButtonPressed()
}
#IBAction func btnNext(_ sender: UIButton) {
print(instanceOfQuizControllerTest)
User.quizIndexNo += 1
Sequence.instanceOfQuizControllerTest.nextButtonPressed()
}
}
Just learning so I am sure there are much better ways to code what I have so far. Anyways, I have a simple task list that is saved to core data. You can segue to another VC where you can edit or create new task. I wanted to be able to put a uiswitch in the custom cell that would allow you to set the task as complete and then reload the tableview. I have searched all over and found this Select UITableView's row when tapping on its UISwitch in Swift but can't seem to translate a solution. Any help would be great.
enter image description here
I am not sure if I am supposed to code the save within the custom cell or just or pass delegate.
Here is my custom cell
protocol SettingCellDelegate : class {
func didChangeSwitchState(sender: TaskCell, isOn: Bool)
}
class TaskCell: UITableViewCell {
var delegate: SettingCellDelegate!
#IBOutlet weak var taskImage: UIImageView!
#IBOutlet weak var arrowImage: UIImageView!
#IBOutlet weak var dueDateLabel: UILabel!
#IBOutlet weak var dueDateValue: UILabel!
#IBOutlet weak var dueTimeLabel: UILabel!
#IBOutlet weak var dueTimeValue: UILabel!
#IBOutlet var taskCellStatus: UITextField!
#IBOutlet var taskCellSwitchDisplay: UISwitch!
#IBAction func taskCellSwitchAction(sender: AnyObject) {
if (taskCellSwitchDisplay.on) {
taskCellStatus.text = "Completed"
} else {
taskCellStatus.text = "Active"
}
}
#IBOutlet weak var taskNameValue: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
delegate = self
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
taskCellSwitchDisplay.addTarget(self, action: "switchChanged:", forControlEvents: UIControlEvents.ValueChanged)
}
func configureCell(task: Tasks) {
taskImage.image = UIImage(named: "Icon.png")
arrowImage.image = UIImage(named: "arrow_filled.png")
dueDateLabel.text = "Due Date:"
dueDateValue.text = task.taskDate
dueTimeLabel.text = "Due Time:"
dueTimeValue.text = task.taskTime
taskNameValue.text = task.taskName
taskCellStatus.text = task.status
//taskCellStatus.hidden = true
if (taskCellStatus.text == "Completed") {
taskCellSwitchDisplay.on = true
} else {
taskCellSwitchDisplay.on = false
}
}
func switchChanged(taskCellSwitchDisplay: UISwitch) {
_ = taskCellSwitchDisplay.on
if taskCellSwitchDisplay.on == true {
let switchValue = "Completed"
print("Switch Variable: \(switchValue)")
delegate.didChangeSwitchState(self, isOn: true)
} else {
let switchValue = "Active"
print("Switch Variable: \(switchValue)")
delegate.didChangeSwitchState(self, isOn: false)
}
}
Here is where I am getting the delegate in my tableview
extension TaskCell: SettingCellDelegate {
func didChangeSwitchState(sender: TaskCell, isOn: Bool) {
if isOn == true {
print("true")
} else {
print("false")
}
}
I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.