Reputation: 105
I am having a problem with creating a nice layout with accomplishing what I want. I want to have a question (I don't need help with this) and 4 answer choices below. I have created these answer choices as Text Fields because that allows me to get a nice border to it that is thin and like I want it to be. But I also want this to be uneditable at the same time clickable. What I mean by this is that the user cannot edit an answer, but when they click on the right answer, the border becomes green or something. I have searched so much, but I always get errors and it never works.
This is an example of the type of layout I want.
I can't make this into a button because it has really ugly and square borders, but I can't do UITextView because I can't make an @IBAction and change specific attributes when clicked. I can't do UITextField because when I click a text field, it doesn't do anything. I can't turn off User Interaction Enabled because then they won't be able to click it. How can I make it work so that it will become like this when clicked but maintains this same format?
(Don't worry about font differences and all.)
I don't have any code to show because there is nothing I have found to work.
Thank you in advance!
Upvotes: 1
Views: 433
Reputation: 12582
It's this easy ...
///Beautiful combo of modern popup menu with attractive standard UItextField
class GroovyUITextField: UITextField {
lazy var butter: UIButton = {
let v = UIButton(type: .custom)
v.configuration = .borderless()
v.setTitle("", for: [])
addSubview(v)
let choices = [
("G", "Very ..."),
("U", "Custom ..."),
("U", "Entry ..."),
("U", "Fields ..."),
("U", "So sweet ..."),
]
let elements: [UIMenuElement] = choices.map({ c in
return UIAction(title: c.1) { [weak self] _ in
guard let self else { return }
self.didSelectGroovy(tup: c)
}
})
v.menu = UIMenu(children: elements)
v.isEnabled = true
v.showsMenuAsPrimaryAction = true
return v
}()
func didSelectGroovy(tup: (String, String)) {
text = tup.1 + " custom control under construction"
}
override func layoutSubviews() {
super.layoutSubviews()
butter.frame = bounds
}
}
Upvotes: 0
Reputation: 409
First create outlet for all button, see the image below
then
@IBAction func languageSelectionButtonPressed(_ sender: UIButton) {
englishBtn.isSelected = false
FrenchBtn.isSelected = false
sender.isSelected = true
}
Upvotes: 0
Reputation: 402
You could use a plain view, where you can adjust the border and corner radius to make it look how you want it. Put a label inside of it and again set the styling you need/want. To detect touch you could either put a button over it (without any text, so it's not visible) or you could add a UITapGestureRecognizer
to the view and use it to detect taps.
This is how you would achieve the desired border look on a view:
view.layer.borderColor = UIColor.lightGray.cgColor
view.layer.borderWidth = 1.0
view.layer.cornerRadius = 5.0
And this is how you would add a gesture recognizer:
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(gestureRecognized(gesture:)))
view.addGestureRecognizer(tapGesture)
// Somewhere below in code put the method that is called when gesture is recognized
@objc
func gestureRecognized(gesture: UITapGestureRecognizer) {
// Gesture recognized
}
Here is a short example how you should set things up:
import UIKit
protocol AnswerViewDelegate: AnyObject {
func answerViewUserDidTap(sender: AnswerView)
}
class AnswerView: UIView {
weak var delegate: AnswerViewDelegate?
private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(gesture:)))
var answerId: String?
var answer: String?
override func awakeFromNib() {
super.awakeFromNib()
layer.borderColor = UIColor.lightGray.cgColor
layer.borderWidth = 1.0
layer.cornerRadius = 5.0
addGestureRecognizer(tapGestureRecognizer)
}
@objc private func tapGestureRecognized(gesture: UITapGestureRecognizer) {
delegate?.answerViewUserDidTap(sender: self)
}
}
class ViewController: UIViewController {
@IBOutlet private weak var answerView1: AnswerView!
@IBOutlet private weak var answerView2: AnswerView!
@IBOutlet private weak var answerView3: AnswerView!
@IBOutlet private weak var answerView4: AnswerView!
override func viewDidLoad() {
super.viewDidLoad()
// This is just for demonstration purposes, to keep the code as short as possible
[answerView1, answerView2, answerView3, answerView4].enumerated().forEach { offset, answerView in
answerView?.answerId = String(offset)
answerView?.answer = "Answer number: \(offset)"
answerView?.delegate = self
}
}
}
// MARK: - AnswerViewDelegate
extension ViewController: AnswerViewDelegate {
func answerViewUserDidTap(sender: AnswerView) {
print("User tapped answer: \(String(describing: sender.answerId)) with id: \(String(describing: sender.answerId)).")
}
}
Upvotes: 0
Reputation: 4860
You can achieve what you want with a custom UIButton
class.
import UIKit
class CustomButton: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
layer.borderWidth = 1.0
layer.borderColor = UIColor.systemGray4.cgColor
layer.cornerRadius = 5.0
contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 0.0)
}
}
That will get you a button with a border and inset which looks like what you are trying to achieve:
A good way of checking whether the correct answer button is tapped would be to use the tag
property on the UIButton
. For example, 0 for the wrong answer, 1 for the correct answer. You can then check the tag value when each button is pressed. (all buttons connected to the same IBAction
). If the correct button is pressed, you can set the border color to green, and do whatever else you need, before repeating the cycle.
import UIKit
class ViewController: UIViewController {
@IBOutlet var answerAButton: CustomButton!
@IBOutlet var answerBButton: CustomButton!
@IBOutlet var answerCButton: CustomButton!
@IBOutlet var answerDButton: CustomButton!
var answerButtons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
answerButtons = [answerAButton, answerBButton, answerCButton, answerDButton]
setCorrectAnswer()
}
func setCorrectAnswer() {
answerButtons.forEach {
// Reset the border on each button
$0.layer.borderColor = UIColor.systemGray4.cgColor
// Reset the tag on each button
$0.tag = 0
}
// Set the correct answer button tag
answerButtons[2].tag = 1
}
@IBAction func answerTapped(_ sender: UIButton) {
if sender.tag == 1 {
sender.layer.borderColor = UIColor.green.cgColor
}
}
}
Answering your additional questions:
1. Where would I put the Custom Button?
2. What type of file should I create for that?
IBOutlets
for your buttons are of type CustomButton
for your IBOutlet
connection. As shown above. You will not see the effects in Storyboard, only at runtime.Upvotes: 1