Can not focus (click) on controls in container blackberry cascades - blackberry-10

I have a Page like below :
import bb.cascades 1.0
NavigationPane {
id: nav
Page {
// Root container
Container {
layout: DockLayout {
}
// Background
ImageView {
imageSource: "asset:///bg.png"
verticalAlignment: VerticalAlignment.Fill
horizontalAlignment: HorizontalAlignment.Fill
}
// Container for contents
Container {
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
// Top navigation : Login and Register buttons
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
ImageView {
id: tabLogin
imageSource: "asset:///ic_login_selected_tab.png"
onTouch: {
// Show login & hide register
loginContainer.setVisible(true)
registerContainer.setVisible(false)
// Change backgrounds
tabLogin.setImageSource(qsTr("asset:///ic_login_selected_tab.png"))
tabRegister.setImageSource(qsTr("asset:///ic_register_normal_tab.png"))
}
}
ImageView {
id: tabRegister
imageSource: "asset:///ic_register_normal_tab.png"
onTouch: {
loginContainer.setVisible(false)
registerContainer.setVisible(true)
tabLogin.setImageSource(qsTr("asset:///ic_login_normal_tab.png"))
tabRegister.setImageSource(qsTr("asset:///ic_register_selected_tab.png"))
}
}
}
// Container for Login and Register pages
Container {
horizontalAlignment: HorizontalAlignment.Fill
layout: DockLayout {
}
// Login container
Container {
id: loginContainer
visible: true
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
topPadding: 200
// safeMATE text
ImageView {
verticalAlignment: VerticalAlignment.Top
horizontalAlignment: HorizontalAlignment.Center
imageSource: "asset:///safemate.png"
}
// Container for email, passcode and login button
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Center
topMargin: 100
bottomMargin: 50.0
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
horizontalAlignment: HorizontalAlignment.Fill
leftPadding: 20.0
rightPadding: 30.0
topPadding: 25
bottomPadding: 25
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_email.png"
}
TextField {
hintText: "Email"
inputMode: TextFieldInputMode.EmailAddress
}
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
horizontalAlignment: HorizontalAlignment.Fill
leftPadding: 20.0
rightPadding: 30.0
topPadding: 25
bottomPadding: 25
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_passcode.png"
}
TextField {
hintText: "Passcode"
inputMode: TextFieldInputMode.Password
}
}
ImageButton {
horizontalAlignment: HorizontalAlignment.Fill
defaultImageSource: "asset:///btn_login.png"
pressedImageSource: "asset:///btn_login_clicked.png"
onClicked: {
var page = homePage.createObject();
nav.push(page);
}
}
}
}
// Register container
ScrollView {
Container {
id: registerContainer
visible: false
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
topPadding: 30
// safeMATE text
ImageView {
verticalAlignment: VerticalAlignment.Top
horizontalAlignment: HorizontalAlignment.Center
imageSource: "asset:///safemate.png"
}
// Container for email, passcode and login button
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Center
topMargin: 30
bottomMargin: 50.0
// Email
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
horizontalAlignment: HorizontalAlignment.Fill
leftPadding: 20.0
rightPadding: 30.0
topPadding: 25
bottomPadding: 25
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_email.png"
}
TextField {
hintText: "Email"
inputMode: TextFieldInputMode.EmailAddress
}
}
// Full name
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
horizontalAlignment: HorizontalAlignment.Fill
leftPadding: 20.0
rightPadding: 30.0
topPadding: 25
bottomPadding: 25
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_fullname.png"
}
TextField {
hintText: "Full name"
inputMode: TextFieldInputMode.Default
}
}
// Passcode
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
horizontalAlignment: HorizontalAlignment.Fill
leftPadding: 20.0
rightPadding: 30.0
topPadding: 25
bottomPadding: 25
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_passcode.png"
}
TextField {
hintText: "Passcode"
inputMode: TextFieldInputMode.Password
}
}
// Phone
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
// Country code
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
leftPadding: 20.0
rightPadding: 30.0
topPadding: 35
bottomPadding: 35
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_phonenumber.png"
}
}
// Phone
Container {
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
leftPadding: 20.0
rightPadding: 30.0
topPadding: 23
bottomPadding: 23
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
// Phone number text box
TextField {
hintText: "Phone number"
inputMode: TextFieldInputMode.Default
}
}
}
// Contact info title
Container {
topMargin: 20
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
ImageView {
imageSource: "asset:///ic_contactinfo.png"
}
Container {
leftPadding: 20
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
Label {
verticalAlignment: VerticalAlignment.Center
text: "CONTACT INFO"
}
Label {
text: "These information will be used for emergencies"
multiline: true
textStyle.fontSize: FontSize.XSmall
}
}
}
// Contact info container
Container {
topMargin: 20
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Center
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
// Phone
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
// Country code
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
leftPadding: 20.0
rightPadding: 30.0
topPadding: 35
bottomPadding: 35
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_phonenumber.png"
}
}
// Phone
Container {
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
leftPadding: 20.0
rightPadding: 30.0
topPadding: 23
bottomPadding: 23
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
// Phone number text box
TextField {
hintText: "Phone number"
inputMode: TextFieldInputMode.Default
}
}
}
// Address
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
background: Color.White
horizontalAlignment: HorizontalAlignment.Fill
leftPadding: 20.0
rightPadding: 30.0
topPadding: 25
bottomPadding: 25
bottomMargin: 5.0
topMargin: 5.0
leftMargin: 5.0
rightMargin: 5.0
layoutProperties: StackLayoutProperties {
}
ImageView {
imageSource: "asset:///ic_address.png"
}
TextField {
hintText: "Address"
inputMode: TextFieldInputMode.EmailAddress
}
}
}
// Register button
ImageButton {
horizontalAlignment: HorizontalAlignment.Fill
defaultImageSource: "asset:///btn_register.png"
pressedImageSource: "asset:///btn_register_clicked.png"
onClicked: {
var page = homePage.createObject();
nav.push(page);
}
}
}
}
}
}
}
}
}
attachedObjects: [
ComponentDefinition {
id: homePage
source: "home.qml"
}
]
onPopTransitionEnded: {
// Transition is done destroy the Page to free up memory.
page.destroy();
}
}
I have 2 buttons on top of page to navigate between Login and Register.
The problem is : I can not focus (input texts or press on button) on Login container (But Register container is working well).
Please let me know how to solve this problem

If I put it in IDE I immediately get
The default property for the component 'Page' does not accept several objects.
So, put a Container around your two containers, so Page will only have one element inside.
import bb.cascades 1.0
Page {
Container {
// Login container
Container {
// Email
TextField {
}
// Password
TextField {
}
// Button login
Button {
}
}
// Register container
Container {
// Email
TextField {
}
// Password
TextField {
}
// Button register
Button {
}
}
}
}
EDIT:
Since you added full code, there is another problem there. You created your Login Container, and then you create "ScrollView" which is for registering new user. The problem ScrollView was created on top of login Container, and even though it isn't visible it blocks container below. Two solutions:
Create a separate page for registering new user
Move your login Container UNDER ScrollView. With a bit of luck that should do the job (and not disable registering). Since you use DockLayout that shouldn't destroy your design

Related

How do I change the border color of a Picker

I have a Picker in my View and I don't know how to access the border color. This is the code:
Picker("Select a Project", selection: $selectedProjectString){
ForEach(projects, id: \.self) {
Text($0).lineLimit(1).frame(height:120)
}
}.foregroundColor(Color.white)
.frame(height: 120)
The Picker looks like this and I want the green border to be white too.
Thanks
Update:
You can achieve this with a custom extension. You can set your border color to any color. (code is below the image)
Credit to #Tamas
Tested view:
Picker("Select a Project", selection: $pick){
ForEach(animal, id: \.self) {
Text($0).lineLimit(1).frame(height:120)
}
}
.focusBorderColor(color: .red)
.foregroundColor(Color.white)
.frame(height: 120)
Extension:
extension Picker {
func focusBorderColor(color: Color) -> some View {
let isWatchOS7: Bool = {
if #available(watchOS 7, *) {
return true
}
return false
}()
let padding: EdgeInsets = {
if isWatchOS7 {
return .init(top: 17, leading: 0, bottom: 0, trailing: 0)
}
return .init(top: 8.5, leading: 0.5, bottom: 8.5, trailing: 0.5)
}()
return self
.overlay(
RoundedRectangle(cornerRadius: isWatchOS7 ? 8 : 7)
.stroke(color, lineWidth: isWatchOS7 ? 4 : 3.5)
.offset(y: isWatchOS7 ? 0 : 8)
.padding(padding)
)
}
}

Selected and unselected from HStack logic

i want that when i pressed on 5th box then all left box will fill , and when i click on eg. 1 then unselected last 4 box , but not 1,
if i clicked on 2nd box then last 3 will be unselected , Thank you in Advanced
#State var SelectedAppsname = [1,2]
ForEach (1..<6){ index in
Button(action: {
let count = selectedAppsName.count
print(count, "count")
print(index,"index")
if selectedAppsName.contains(index) {
print(index, "inINdex")
for i in 0..<count {
if (index<=i) {
}
else {
let new = count - index
print(new, "new")
selectedAppsName.removeLast(new)
}
}
}
else {
for i in count + 1...index {
selectedAppsName.append(i)
}
}
}, label: {
RoundedRectangle(cornerRadius: 16).fill(Color.secondary.opacity(0.5))
.frame(width: 49.4, height: 56, alignment: .center)
})
.background(RoundedRectangle(cornerRadius: 16).fill( selectedAppsName.contains(index) ? Color(red: 1.0, green: 0.9, blue: 0.02) : Color.clear))
}
}
Make it simple:
#State private var level = 0
var body: some View {
HStack {
ForEach(0..<6) { index in
Button {
withAnimation {
level = index
}
} label: {
RoundedRectangle(cornerRadius: 5)
.foregroundColor(index > level ? .gray : .yellow)
.frame(width: 49.4, height: 56, alignment: .center)
}
}
}
}

Animation going put effect on another animation in SwiftUI-macOS

I have 2 deferent and separate Animation in my code for SwiftUI-macOS, I tried this down code, but the animation about opacity slow down the animation about value for some reason, that effect is not what I want or need! I want the animation about opacity take it place while it works smoothly with animation about value. What could be the issue? I need to refactor my code as they are to solve the animation issue. Xcode: Version 13.2.1 (13C100)
struct ContentView: View {
#State private var value: Bool = true
var body: some View {
VStack {
Group {
if value { Color.green.frame(height: 50).transition(AnyTransition.slide) }
}
.animation(Animation.linear(duration: 2.0), value: value)
HStack {
ButtonView(color: .red, action: { value.toggle() })
ButtonView(color: .blue, action: { value.toggle() })
}
}
}
}
struct ButtonView: View {
let color: Color
let action: () -> Void
#State private var opacity: CGFloat = 1.0
var body: some View {
color
.frame(height: 50)
.opacity(opacity)
.onTapGesture {
if opacity == 1.0 { opacity = 0.2 }
else { opacity = 1.0 }
action()
}
.animation(Animation.linear(duration: 5.0), value: opacity)
}
}
The Group is not real container, actually, it is better to use instead a V/HStack with preserved space, like
VStack {
if value { Color.green.transition(AnyTransition.slide) }
}
.frame(height: 50) // << here !1
.animation(Animation.linear(duration: 2.0), value: value)
Tested with Xcode 13.2 / iOS 15.2

Disable animation when a view appears in SwiftUI

I got a problem while trying to display a custom loading view in SwiftUI.
I created a custom struct view OrangeActivityIndicator:
struct OrangeActivityIndicator: View {
var style = StrokeStyle(lineWidth: 6, lineCap: .round)
#State var animate = false
let orangeColor = Color.orOrangeColor
let orangeColorOpaque = Color.orOrangeColor.opacity(0.5)
init(lineWidth: CGFloat = 6) {
style.lineWidth = lineWidth
}
var body: some View {
ZStack {
Circle()
.trim(from: 0, to: 0.7)
.stroke(
AngularGradient(gradient: .init(colors: [orangeColor, orangeColorOpaque]), center: .center), style: style
)
.rotationEffect(Angle(degrees: animate ? 360 : 0))
.animation(Animation.linear(duration: 0.7).repeatForever(autoreverses: false))
}.onAppear() {
self.animate.toggle()
}
}
}
I use it inside different screens or views, my problem is that it appears weirdly, for example in CampaignsView of the app I display it when the server call is in progress.
struct CampaignsView: View {
#ObservedObject var viewModel: CampaignsViewModel
var body: some View {
NavigationView {
ZStack {
VStack(spacing: 0) {
CustomNavigationBar(campaignsNumber: viewModel.cardCampaigns.count)
.padding([.leading, .trailing], 24)
.frame(height: 25)
CarouselView(x: $viewModel.x, screen: viewModel.screen, op: $viewModel.op, count: $viewModel.index, cardCampaigns: $viewModel.cardCampaigns).frame(height: 240)
CampaignDescriptionView(idx: viewModel.index, cardCampaigns: viewModel.cardCampaigns)
.padding([.leading, .trailing], 24)
Spacer()
}
.onAppear {
self.viewModel.getCombineCampaigns()
}
if viewModel.isLoading {
OrangeActivityIndicator()
.frame(width: 40, height: 40)
}
}
.padding(.top, 34)
.background(Color.orBackgroundGrayColor.edgesIgnoringSafeArea(.all))
.navigationBarHidden(true)
}
}
}
The Indicator itself is correctly spinning, the problem is when it appears, it appears as a translation animation coming from the bottom to the middle of the screen. This is my viewModel with the server call and isLoading property:
class CampaignsViewModel: ObservableObject {
#Published var index: Int = 0
#Published var cardCampaigns: [CardCampaign] = [CardCampaign]()
#Published var isLoading: Bool = false
var cancellable: AnyCancellable?
func getCombineCampaigns() {
self.isLoading = true
let campaignLoader = CampaignLoader()
cancellable = campaignLoader.getCampaigns()
.receive(on: DispatchQueue.main)
//Handle Events operator is used for debugging.
.handleEvents(receiveSubscription: { print("Receive subscription: \($0)") },
receiveOutput: { print("Receive output: \($0)") },
receiveCompletion: { print("Receive completion: \($0)") },
receiveCancel: { print("Receive cancel") },
receiveRequest: { print("Receive request: \($0)") })
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
print(error)
}
} receiveValue: { campaignResult in
self.isLoading = false
guard let campaignsList = campaignResult.content else {
return
}
self.cardCampaigns = campaignsList.map { campaign in
return CardCampaign(campaign: campaign)
}
self.moveToFirstCard()
}
}
}
Use animation with linked related state
var body: some View {
ZStack {
Circle()
.trim(from: 0, to: 0.7)
.stroke(
AngularGradient(gradient: .init(colors: [orangeColor, orangeColorOpaque]), center: .center), style: style
)
.rotationEffect(Angle(degrees: animate ? 360 : 0))
.animation(Animation.linear(duration: 0.7)
.repeatForever(autoreverses: false),
value: animate) // << here !!
}.onAppear() {
self.animate.toggle()
}
}
SwiftUI 3
(iOS 15)
use the new API
.animation(.default, value: isAppeared)
SwiftUI 1, 2
Like below, when I first load a view, the button was animated.
So, I used the GeometryReader.
The animation is set to nil while the coordinates of the dummy are changing.
RecordView.swift
struct RecordView: View {
var body: some View {
Button(action: { }) {
Color(.red)
.frame(width: 38, height: 38)
.cornerRadius(6)
.padding(34)
.overlay(Circle().stroke(Color(.red), lineWidth: 8))
}
.buttonStyle(ButtonStyle1()) // 👈 customize the button.
}
}
ButtonStyle.swift
import SwiftUI
struct ButtonStyle1: ButtonStyle {
// In my case, the #State isn't worked, so I used class.
#ObservedObject private var data = ButtonStyle1Data()
func makeBody(configuration: Self.Configuration) -> some View {
ZStack {
// Dummy for tracking the view status
GeometryReader {
Color.clear
.preference(key: FramePreferenceKey.self, value: $0.frame(in: .global))
}
.frame(width: 0, height: 0)
.onPreferenceChange(FramePreferenceKey.self) { frame in
guard !data.isAppeared else { return }
// ⬇️ This is the key
data.isLoaded = 0 <= frame.origin.x
}
// Content View
configuration.label
.opacity(configuration.isPressed ? 0.5 : 1)
.scaleEffect(configuration.isPressed ? 0.92 : 1)
.animation(data.isAppeared ? .easeInOut(duration: 0.18) : nil)
}
}
}
ButtonStyleData.swift
final class ButtonStyle1Data: ObservableObject {
#Published var isAppeared = false
}
FramePreferenceKey.swift
struct FramePreferenceKey: PreferenceKey {
static var defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {}
}
Result

How to update the content inside the ScrollView : SwiftUI

I am trying to update my content inside the scroll view but since makeUIView called only once when view builds. It doesn't refresh when I click on 1 image and then 2 images.
However, If I click on No image and then click on Image 2 it works as expected.
Here is my code.
struct ContentView: View {
#State var images:[String] = []
var body: some View {
VStack{
HStack{
Button(action: { self.images = [] }, label: {
Text("No Images")
})
Button(action: { self.images = ["sample_1"] }, label: {
Text("1 Images")
})
Button(action: { self.images = ["sample_1","sample_2"] }, label: {
Text("2 Images")
})
}
if self.images.count > 0{
GeometryReader { (reader) in
STHorizontalScrollView(images: self.images, size: reader.size)
}
}else{
Text("No Images")
}
}
}
}
struct STHorizontalScrollView:UIViewRepresentable {
var images:[String]
var size:CGSize
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
// Calculating the Width
let widthForContent = CGFloat(self.images.count) * size.width
scrollView.contentSize = CGSize.init(width: widthForContent, height: size.height)
scrollView.isPagingEnabled = true
// Hosting Controller
let hostingController = UIHostingController(rootView: HorizontalList(images: images, size: size))
hostingController.view.frame = CGRect.init(x: 0, y: 0, width: widthForContent, height: size.height)
// Add View to scrollview
scrollView.addSubview(hostingController.view)
return scrollView
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
}
}
struct HorizontalList:View {
var images:[String]
var size:CGSize
var body: some View{
HStack(spacing: 0.0) {
ForEach(self.images, id:\.self){ image in
Image(image)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(width: self.size.width, height: self.size.height)
.clipped()
}
}
}
}
Not sure what to write here in the update method as the actual content is inside HorizontalList struct.
Any help will be appreciated.
Thanks
Representable is updated whenever some binding of dependent data is updated, so you need to rebuild internals in update updateUIView when images changes.
Here is a solution. Tested with Xcode 11.4 / iOS 13.4
Usage of representable
// images as binding
STHorizontalScrollView(images: self.$images, size: reader.size)
and representable itself
struct STHorizontalScrollView:UIViewRepresentable {
#Binding var images:[String]
var size:CGSize
func makeUIView(context: Context) -> UIScrollView {
UIScrollView()
}
func updateUIView(_ scrollView: UIScrollView, context: Context) {
_ = scrollView.subviews.map { $0.removeFromSuperview() }
guard images.count != 0 else { return }
// Calculating the Width
let widthForContent = CGFloat(self.images.count) * size.width
scrollView.contentSize = CGSize.init(width: widthForContent, height: size.height)
scrollView.isPagingEnabled = true
// Hosting Controller
let hostingController = UIHostingController(rootView: HorizontalList(images: images, size: size))
hostingController.view.frame = CGRect.init(x: 0, y: 0, width: widthForContent, height: size.height)
// Add View to scrollview
scrollView.addSubview(hostingController.view)
}
}